blob: 5b17ee1b272e756b6b75054dacf7d13e3456c912 [file] [log] [blame]
/*
Copyright 2019 Bloomberg Finance LP.
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"
"k8s.io/apimachinery/pkg/runtime"
"reflect"
"time"
"github.com/bloomberg/solr-operator/controllers/util"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
solrv1beta1 "github.com/bloomberg/solr-operator/api/v1beta1"
)
// SolrCollectionReconciler reconciles a SolrCollection object
type SolrCollectionReconciler struct {
client.Client
scheme *runtime.Scheme
Log logr.Logger
}
// +kubebuilder:rbac:groups=solr.bloomberg.com,resources=solrcollections,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=solr.bloomberg.com,resources=solrcollections/status,verbs=get;update;patch
func (r *SolrCollectionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
_ = r.Log.WithValues("solrcollection", req.NamespacedName)
// Fetch the SolrCollection collection
collection := &solrv1beta1.SolrCollection{}
collectionFinalizer := "collection.finalizers.bloomberg.com"
err := r.Get(context.TODO(), req.NamespacedName, collection)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the req.
return reconcile.Result{}, err
}
oldStatus := collection.Status.DeepCopy()
requeueOrNot := reconcile.Result{Requeue: true, RequeueAfter: time.Second * 5}
solrCloud, collectionCreationStatus, err := reconcileSolrCollection(r, collection, collection.Spec.NumShards, collection.Spec.ReplicationFactor, collection.Spec.AutoAddReplicas, collection.Spec.MaxShardsPerNode, collection.Spec.RouterName, collection.Spec.RouterField, collection.Spec.Shards, collection.Spec.CollectionConfigName, collection.Namespace)
if err != nil {
r.Log.Error(err, "Error while creating SolrCloud collection")
return reconcile.Result{}, err
}
if collection.Status.CreatedTime == nil {
now := metav1.Now()
collection.Status.CreatedTime = &now
}
if solrCloud != nil && collectionCreationStatus == false {
r.Log.Info("Collections update failed")
}
if collection.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object
if !util.ContainsString(collection.ObjectMeta.Finalizers, collectionFinalizer) {
collection.ObjectMeta.Finalizers = append(collection.ObjectMeta.Finalizers, collectionFinalizer)
if err := r.Update(context.Background(), collection); err != nil {
return reconcile.Result{}, err
}
}
} else {
// The object is being deleted
if util.ContainsString(collection.ObjectMeta.Finalizers, collectionFinalizer) {
r.Log.Info("Deleting Solr collection", "cloud", collection.Spec.SolrCloud, "namespace", collection.Namespace, "Collection Name", collection.Name)
// our finalizer is present, so lets handle our external dependency
delete, err := util.DeleteCollection(collection.Spec.SolrCloud, collection.Name, collection.Namespace)
if err != nil {
r.Log.Error(err, "Failed to delete Solr collection")
return reconcile.Result{}, err
}
r.Log.Info("Deleted Solr collection", "cloud", collection.Spec.SolrCloud, "namespace", collection.Namespace, "Collection Name", collection.Name, "Deleted", delete)
}
// remove our finalizer from the list and update it.
collection.ObjectMeta.Finalizers = util.RemoveString(collection.ObjectMeta.Finalizers, collectionFinalizer)
if err := r.Update(context.Background(), collection); err != nil {
return reconcile.Result{}, err
}
}
if !reflect.DeepEqual(oldStatus, collection.Status) {
r.Log.Info("Updating status for solr-collection", "collection", collection, "namespace", collection.Namespace, "name", collection.Name)
err = r.Status().Update(context.TODO(), collection)
}
if collection.Status.InProgressCreation {
if util.CheckIfCollectionExists(collection.Spec.SolrCloud, collection.Spec.Collection, collection.Namespace) {
r.Log.Info("Collection exists, creation complete", "collection", collection, "namespace", collection.Namespace, "name", collection.Name)
collection.Status.InProgressCreation = false
requeueOrNot = reconcile.Result{}
} else {
r.Log.Info("Collection creation still in progress", "collection", collection, "namespace", collection.Namespace, "name", collection.Name)
requeueOrNot = reconcile.Result{Requeue: true}
}
}
if collection.Status.Created {
requeueOrNot = reconcile.Result{}
}
return requeueOrNot, nil
}
func reconcileSolrCollection(r *SolrCollectionReconciler, collection *solrv1beta1.SolrCollection, numShards int64, replicationFactor int64, autoAddReplicas bool, maxShardsPerNode int64, routerName string, routerField string, shards string, collectionConfigName string, namespace string) (solrCloud *solrv1beta1.SolrCloud, collectionCreationStatus bool, err error) {
// Get the solrCloud that this collection is for.
solrCloud = &solrv1beta1.SolrCloud{}
if err = r.Get(context.TODO(), types.NamespacedName{Namespace: collection.Namespace, Name: collection.Spec.SolrCloud}, solrCloud); err != nil {
return nil, false, err
}
// If the collection has already been created already and requires modification
if collection.Status.Created {
modificationRequired, err := util.CheckIfCollectionModificationRequired(solrCloud.Name, collection.Name, replicationFactor, autoAddReplicas, maxShardsPerNode, collectionConfigName, namespace)
if err != nil {
return nil, false, err
}
if modificationRequired {
modify, err := util.ModifyCollection(solrCloud.Name, collection.Name, replicationFactor, autoAddReplicas, maxShardsPerNode, collectionConfigName, namespace)
if err != nil {
return nil, false, err
}
r.Log.Info("Modified Solr collection", "SolrCloud", collection.Spec.SolrCloud, "namespace", collection.Namespace, "Collection Name", collection.Name, "Modified", modify)
} else {
r.Log.Info("Modification on Solr collection not required", "SolrCloud", collection.Spec.SolrCloud, "namespace", collection.Namespace, "Collection Name", collection.Name, "Modified", nil)
}
}
// If the collection collection hasn't been created or is in progress, start it creation
if !collection.Status.Created && !collection.Status.InProgressCreation {
// Request the creation of collection by calling solr
collection.Status.InProgressCreation = true
create, err := util.CreateCollection(solrCloud.Name, collection.Name, numShards, replicationFactor, autoAddReplicas, routerName, routerField, shards, collectionConfigName, namespace)
if err != nil {
collection.Status.InProgressCreation = false
return nil, false, err
}
collection.Status.Created = create
collection.Status.InProgressCreation = false
}
err = r.Get(context.TODO(), types.NamespacedName{Namespace: collection.Namespace, Name: collection.Spec.SolrCloud}, solrCloud)
return solrCloud, collection.Status.Created, err
}
func (r *SolrCollectionReconciler) SetupWithManager(mgr ctrl.Manager) error {
return r.SetupWithManagerAndReconciler(mgr, r)
}
func (r *SolrCollectionReconciler) SetupWithManagerAndReconciler(mgr ctrl.Manager, reconciler reconcile.Reconciler) error {
ctrlBuilder := ctrl.NewControllerManagedBy(mgr).
For(&solrv1beta1.SolrCollection{})
r.scheme = mgr.GetScheme()
return ctrlBuilder.Complete(reconciler)
}