You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ho...@apache.org on 2021/11/04 17:14:44 UTC
[solr-operator] branch main updated: Specify individual backupRepo
availability in SolrCloud Status (#358)
This is an automated email from the ASF dual-hosted git repository.
houston pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-operator.git
The following commit(s) were added to refs/heads/main by this push:
new 9860911 Specify individual backupRepo availability in SolrCloud Status (#358)
9860911 is described below
commit 9860911ac24479bc23d447a73115d74ae96b4eb2
Author: Houston Putman <ho...@apache.org>
AuthorDate: Thu Nov 4 13:14:39 2021 -0400
Specify individual backupRepo availability in SolrCloud Status (#358)
---
api/v1beta1/solrbackup_types.go | 8 ++
api/v1beta1/solrcloud_types.go | 14 +++-
api/v1beta1/zz_generated.deepcopy.go | 7 ++
config/crd/bases/solr.apache.org_solrbackups.yaml | 6 ++
config/crd/bases/solr.apache.org_solrclouds.yaml | 14 +++-
controllers/solrbackup_controller.go | 86 ++++++++++++++++++----
controllers/solrcloud_controller.go | 44 ++++++-----
controllers/solrcloud_controller_backup_test.go | 54 +++++++++++++-
.../solrprometheusexporter_controller_test.go | 5 +-
controllers/util/solr_api/cluster_status.go | 48 ++++++------
controllers/util/solr_backup_repo_util.go | 28 +++++++
controllers/util/solr_util.go | 2 +
example/test_backup_gcs.yaml | 4 +-
example/test_backup_managed.yaml | 2 +-
example/test_solrcloud_backuprepos.yaml | 27 ++++---
helm/solr-operator/Chart.yaml | 7 ++
helm/solr-operator/crds/crds.yaml | 20 ++++-
17 files changed, 288 insertions(+), 88 deletions(-)
diff --git a/api/v1beta1/solrbackup_types.go b/api/v1beta1/solrbackup_types.go
index e81ee9e..ec0d151 100644
--- a/api/v1beta1/solrbackup_types.go
+++ b/api/v1beta1/solrbackup_types.go
@@ -27,10 +27,18 @@ import (
// SolrBackupSpec defines the desired state of SolrBackup
type SolrBackupSpec struct {
// A reference to the SolrCloud to create a backup for
+ //
+ // +kubebuilder:validation:Pattern:=[a-z0-9]([-a-z0-9]*[a-z0-9])?
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
SolrCloud string `json:"solrCloud"`
// The name of the repository to use for the backup. Defaults to "legacy_local_repository" if not specified (the
// auto-configured repository for legacy singleton volumes).
+ //
+ // +kubebuilder:validation:Pattern:=[a-zA-Z0-9]([-_a-zA-Z0-9]*[a-zA-Z0-9])?
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=100
// +optional
RepositoryName string `json:"repositoryName,omitempty"`
diff --git a/api/v1beta1/solrcloud_types.go b/api/v1beta1/solrcloud_types.go
index 692afd4..ff265ec 100644
--- a/api/v1beta1/solrcloud_types.go
+++ b/api/v1beta1/solrcloud_types.go
@@ -385,6 +385,10 @@ type SolrBackupRestoreOptions struct {
type SolrBackupRepository struct {
// A name used to identify this local storage profile. Values should follow RFC-1123. (See here for more details:
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names)
+ //
+ // +kubebuilder:validation:Pattern:=[a-zA-Z0-9]([-_a-zA-Z0-9]*[a-zA-Z0-9])?
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=100
Name string `json:"name"`
// A GCSRepository for Solr to use when backing up and restoring collections.
@@ -1013,16 +1017,16 @@ type SolrCloudStatus struct {
// SolrNodes contain the statuses of each solr node running in this solr cloud.
SolrNodes []SolrNodeStatus `json:"solrNodes"`
- // Replicas is the number of number of desired replicas in the cluster
+ // Replicas is the number of desired replicas in the cluster
Replicas int32 `json:"replicas"`
// PodSelector for SolrCloud pods, required by the HPA
PodSelector string `json:"podSelector"`
- // ReadyReplicas is the number of number of ready replicas in the cluster
+ // ReadyReplicas is the number of ready replicas in the cluster
ReadyReplicas int32 `json:"readyReplicas"`
- // UpToDateNodes is the number of number of Solr Node pods that are running the latest pod spec
+ // UpToDateNodes is the number of Solr Node pods that are running the latest pod spec
UpToDateNodes int32 `json:"upToDateNodes"`
// The version of solr that the cloud is running
@@ -1047,6 +1051,10 @@ type SolrCloudStatus struct {
// BackupRestoreReady announces whether the solrCloud has the backupRestorePVC mounted to all pods
// and therefore is ready for backups and restores.
BackupRestoreReady bool `json:"backupRestoreReady"`
+
+ // BackupRepositoriesAvailable lists the backupRepositories specified in the SolrCloud and whether they are available across all Pods.
+ // +optional
+ BackupRepositoriesAvailable map[string]bool `json:"backupRepositoriesAvailable,omitempty"`
}
// SolrNodeStatus is the status of a solrNode in the cloud, with readiness status
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 79d356a..31031ee 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -1020,6 +1020,13 @@ func (in *SolrCloudStatus) DeepCopyInto(out *SolrCloudStatus) {
**out = **in
}
in.ZookeeperConnectionInfo.DeepCopyInto(&out.ZookeeperConnectionInfo)
+ if in.BackupRepositoriesAvailable != nil {
+ in, out := &in.BackupRepositoriesAvailable, &out.BackupRepositoriesAvailable
+ *out = make(map[string]bool, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SolrCloudStatus.
diff --git a/config/crd/bases/solr.apache.org_solrbackups.yaml b/config/crd/bases/solr.apache.org_solrbackups.yaml
index ddb9ffe..248515a 100644
--- a/config/crd/bases/solr.apache.org_solrbackups.yaml
+++ b/config/crd/bases/solr.apache.org_solrbackups.yaml
@@ -1053,9 +1053,15 @@ spec:
type: object
repositoryName:
description: The name of the repository to use for the backup. Defaults to "legacy_local_repository" if not specified (the auto-configured repository for legacy singleton volumes).
+ maxLength: 100
+ minLength: 1
+ pattern: '[a-zA-Z0-9]([-_a-zA-Z0-9]*[a-zA-Z0-9])?'
type: string
solrCloud:
description: A reference to the SolrCloud to create a backup for
+ maxLength: 63
+ minLength: 1
+ pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?'
type: string
required:
- solrCloud
diff --git a/config/crd/bases/solr.apache.org_solrclouds.yaml b/config/crd/bases/solr.apache.org_solrclouds.yaml
index 4af3461..29ce391 100644
--- a/config/crd/bases/solr.apache.org_solrclouds.yaml
+++ b/config/crd/bases/solr.apache.org_solrclouds.yaml
@@ -1021,6 +1021,9 @@ spec:
type: object
name:
description: 'A name used to identify this local storage profile. Values should follow RFC-1123. (See here for more details: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names)'
+ maxLength: 100
+ minLength: 1
+ pattern: '[a-zA-Z0-9]([-_a-zA-Z0-9]*[a-zA-Z0-9])?'
type: string
s3:
description: An S3Repository for Solr to use when backing up and restoring collections.
@@ -6927,6 +6930,11 @@ spec:
status:
description: SolrCloudStatus defines the observed state of SolrCloud
properties:
+ backupRepositoriesAvailable:
+ additionalProperties:
+ type: boolean
+ description: BackupRepositoriesAvailable lists the backupRepositories specified in the SolrCloud and whether they are available across all Pods.
+ type: object
backupRestoreReady:
description: BackupRestoreReady announces whether the solrCloud has the backupRestorePVC mounted to all pods and therefore is ready for backups and restores.
type: boolean
@@ -6940,11 +6948,11 @@ spec:
description: PodSelector for SolrCloud pods, required by the HPA
type: string
readyReplicas:
- description: ReadyReplicas is the number of number of ready replicas in the cluster
+ description: ReadyReplicas is the number of ready replicas in the cluster
format: int32
type: integer
replicas:
- description: Replicas is the number of number of desired replicas in the cluster
+ description: Replicas is the number of desired replicas in the cluster
format: int32
type: integer
solrNodes:
@@ -6986,7 +6994,7 @@ spec:
description: The version of solr that the cloud is meant to be running. Will only be provided when the cloud is migrating between versions
type: string
upToDateNodes:
- description: UpToDateNodes is the number of number of Solr Node pods that are running the latest pod spec
+ description: UpToDateNodes is the number of Solr Node pods that are running the latest pod spec
format: int32
type: integer
version:
diff --git a/controllers/solrbackup_controller.go b/controllers/solrbackup_controller.go
index cfee5d4..4e8d41b 100644
--- a/controllers/solrbackup_controller.go
+++ b/controllers/solrbackup_controller.go
@@ -20,7 +20,12 @@ package controllers
import (
"context"
"fmt"
+ "k8s.io/apimachinery/pkg/fields"
"reflect"
+ "sigs.k8s.io/controller-runtime/pkg/builder"
+ "sigs.k8s.io/controller-runtime/pkg/handler"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+ "sigs.k8s.io/controller-runtime/pkg/source"
"time"
"github.com/apache/solr-operator/controllers/util"
@@ -158,12 +163,10 @@ func (r *SolrBackupReconciler) reconcileSolrCloudBackup(ctx context.Context, bac
return solrCloud, actionTaken, err
}
- // Make sure that all solr nodes are active and have the backupRestore shared volume mounted
- // TODO: we do not need all replicas to be healthy. We should just check that leaders exist for all shards. (or just let Solr do that)
- cloudReady := solrCloud.Status.BackupRestoreReady && (solrCloud.Status.Replicas == solrCloud.Status.ReadyReplicas)
- if !cloudReady {
- logger.Info("Cloud not ready for backup backup", "solrCloud", solrCloud.Name)
- return solrCloud, actionTaken, errors.NewServiceUnavailable("Cloud is not ready for backups or restores")
+ // Make sure that all solr living Solr pods have the backupRepo configured
+ if !solrCloud.Status.BackupRepositoriesAvailable[backupRepository.Name] {
+ logger.Info("Cloud not ready for backup", "solrCloud", solrCloud.Name, "repository", backupRepository.Name)
+ return solrCloud, actionTaken, errors.NewServiceUnavailable(fmt.Sprintf("Cloud is not ready for backups in the %s repository", backupRepository.Name))
}
// Only set the solr version at the start of the backup. This shouldn't change throughout the backup.
@@ -173,7 +176,9 @@ func (r *SolrBackupReconciler) reconcileSolrCloudBackup(ctx context.Context, bac
// Go through each collection specified and reconcile the backup.
for _, collection := range backup.Spec.Collections {
// This will in-place update the CollectionBackupStatus in the backup object
- _, err = reconcileSolrCollectionBackup(ctx, backup, solrCloud, backupRepository, collection, logger)
+ if _, err = reconcileSolrCollectionBackup(ctx, backup, solrCloud, backupRepository, collection, logger); err != nil {
+ break
+ }
}
// First check if the collection backups have been completed
@@ -198,7 +203,8 @@ func reconcileSolrCollectionBackup(ctx context.Context, backup *solrv1beta1.Solr
// If the collection backup hasn't started, start it
if !collectionBackupStatus.InProgress && !collectionBackupStatus.Finished {
// Start the backup by calling solr
- started, err := util.StartBackupForCollection(ctx, solrCloud, backupRepository, backup, collection, logger)
+ var started bool
+ started, err = util.StartBackupForCollection(ctx, solrCloud, backupRepository, backup, collection, logger)
if err != nil {
return true, err
}
@@ -208,8 +214,10 @@ func reconcileSolrCollectionBackup(ctx context.Context, backup *solrv1beta1.Solr
}
collectionBackupStatus.BackupName = util.FullCollectionBackupName(collection, backup.Name)
} else if collectionBackupStatus.InProgress {
+ var successful bool
+ var asyncStatus string
// Check the state of the backup, when it is in progress, and update the state accordingly
- finished, successful, asyncStatus, err := util.CheckBackupForCollection(ctx, solrCloud, collection, backup.Name, logger)
+ finished, successful, asyncStatus, err = util.CheckBackupForCollection(ctx, solrCloud, collection, backup.Name, logger)
if err != nil {
return false, err
}
@@ -240,11 +248,63 @@ func reconcileSolrCollectionBackup(ctx context.Context, backup *solrv1beta1.Solr
}
// SetupWithManager sets up the controller with the Manager.
-func (r *SolrBackupReconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *SolrBackupReconciler) SetupWithManager(mgr ctrl.Manager) (err error) {
r.config = mgr.GetConfig()
- return ctrl.NewControllerManagedBy(mgr).
+ ctrlBuilder := ctrl.NewControllerManagedBy(mgr).
For(&solrv1beta1.SolrBackup{}).
- Owns(&batchv1.Job{}).
- Complete(r)
+ Owns(&batchv1.Job{})
+
+ ctrlBuilder, err = r.indexAndWatchForSolrClouds(mgr, ctrlBuilder)
+ if err != nil {
+ return err
+ }
+
+ return ctrlBuilder.Complete(r)
+}
+
+func (r *SolrBackupReconciler) indexAndWatchForSolrClouds(mgr ctrl.Manager, ctrlBuilder *builder.Builder) (*builder.Builder, error) {
+ solrCloudField := ".spec.solrCloud"
+
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &solrv1beta1.SolrBackup{}, solrCloudField, func(rawObj client.Object) []string {
+ // grab the SolrBackup object, extract the used SolrCloud...
+ return []string{rawObj.(*solrv1beta1.SolrBackup).Spec.SolrCloud}
+ }); err != nil {
+ return ctrlBuilder, err
+ }
+
+ return ctrlBuilder.Watches(
+ &source.Kind{Type: &solrv1beta1.SolrCloud{}},
+ handler.EnqueueRequestsFromMapFunc(func(obj client.Object) []reconcile.Request {
+ solrCloud := obj.(*solrv1beta1.SolrCloud)
+ foundBackups := &solrv1beta1.SolrBackupList{}
+ listOps := &client.ListOptions{
+ FieldSelector: fields.OneTermEqualSelector(solrCloudField, obj.GetName()),
+ Namespace: obj.GetNamespace(),
+ }
+ err := r.List(context.Background(), foundBackups, listOps)
+ if err != nil {
+ // if no exporters found, just no-op this
+ return []reconcile.Request{}
+ }
+
+ requests := make([]reconcile.Request, 0)
+ for _, item := range foundBackups.Items {
+ // Only queue the request if the Cloud is ready.
+ cloudIsReady := solrCloud.Status.BackupRestoreReady
+ if item.Spec.RepositoryName != "" {
+ cloudIsReady = solrCloud.Status.BackupRepositoriesAvailable[item.Spec.RepositoryName]
+ }
+ if cloudIsReady {
+ requests = append(requests, reconcile.Request{
+ NamespacedName: types.NamespacedName{
+ Name: item.GetName(),
+ Namespace: item.GetNamespace(),
+ },
+ })
+ }
+ }
+ return requests
+ }),
+ builder.WithPredicates(predicate.GenerationChangedPredicate{})), nil
}
diff --git a/controllers/solrcloud_controller.go b/controllers/solrcloud_controller.go
index b854855..8d0153c 100644
--- a/controllers/solrcloud_controller.go
+++ b/controllers/solrcloud_controller.go
@@ -115,7 +115,7 @@ func (r *SolrCloudReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
newStatus := solrv1beta1.SolrCloudStatus{}
blockReconciliationOfStatefulSet := false
- if err := r.reconcileZk(ctx, logger, instance, &newStatus); err != nil {
+ if err = r.reconcileZk(ctx, logger, instance, &newStatus); err != nil {
return requeueOrNot, err
}
@@ -511,9 +511,12 @@ func (r *SolrCloudReconciler) reconcileCloudStatus(ctx context.Context, solrClou
return outOfDatePods, outOfDatePodsNotStarted, availableUpdatedPodCount, err
}
newStatus.PodSelector = selector.String()
- allPodsBackupReady := true
- for idx, p := range foundPods.Items {
- nodeNames[idx] = p.Name
+ backupReposAvailable := make(map[string]bool, len(solrCloud.Spec.BackupRepositories))
+ for _, repo := range solrCloud.Spec.BackupRepositories {
+ backupReposAvailable[repo.Name] = false
+ }
+ for podIdx, p := range foundPods.Items {
+ nodeNames[podIdx] = p.Name
nodeStatus := solrv1beta1.SolrNodeStatus{}
nodeStatus.Name = p.Name
nodeStatus.NodeName = p.Spec.NodeName
@@ -540,9 +543,10 @@ func (r *SolrCloudReconciler) reconcileCloudStatus(ctx context.Context, solrClou
newStatus.ReadyReplicas += 1
}
- // Skip "backup-readiness" check for pod if we've already found a pod that's not ready
- if allPodsBackupReady {
- allPodsBackupReady = allPodsBackupReady && isPodReadyForBackup(&p, solrCloud)
+ // Merge BackupRepository availability for this pod
+ backupReposAvailableForPod := util.GetAvailableBackupRepos(&p)
+ for repo, availableSoFar := range backupReposAvailable {
+ backupReposAvailable[repo] = (availableSoFar || podIdx == 0) && backupReposAvailableForPod[repo]
}
// A pod is out of date if it's revision label is not equal to the statefulSetStatus' updateRevision.
@@ -581,10 +585,16 @@ func (r *SolrCloudReconciler) reconcileCloudStatus(ctx context.Context, solrClou
for idx, nodeName := range nodeNames {
newStatus.SolrNodes[idx] = nodeStatusMap[nodeName]
}
- if allPodsBackupReady && len(foundPods.Items) > 0 {
- newStatus.BackupRestoreReady = true
- } else {
- newStatus.BackupRestoreReady = false
+ if len(backupReposAvailable) > 0 {
+ newStatus.BackupRepositoriesAvailable = backupReposAvailable
+ allPodsBackupReady := len(backupReposAvailable) > 0
+ for _, backupRepo := range solrCloud.Spec.BackupRepositories {
+ allPodsBackupReady = allPodsBackupReady && backupReposAvailable[backupRepo.Name]
+ if !allPodsBackupReady {
+ break
+ }
+ }
+ newStatus.BackupRestoreReady = allPodsBackupReady
}
// If there are multiple versions of solr running, use the first otherVersion as the current running solr version of the cloud
@@ -605,18 +615,6 @@ func (r *SolrCloudReconciler) reconcileCloudStatus(ctx context.Context, solrClou
return outOfDatePods, outOfDatePodsNotStarted, availableUpdatedPodCount, nil
}
-func isPodReadyForBackup(pod *corev1.Pod, solrCloud *solrv1beta1.SolrCloud) bool {
- // If solrcloud doesn't request backup support then everything is 'ready' implicitly
- if len(solrCloud.Spec.BackupRepositories) == 0 {
- return false
- }
-
- // TODO: There is no way to possibly do this with the new S3 option.
- // This is wrong, but not the end of the world.
- // Replace with new functionality in https://github.com/apache/solr-operator/issues/326
- return true
-}
-
func (r *SolrCloudReconciler) reconcileNodeService(ctx context.Context, logger logr.Logger, instance *solrv1beta1.SolrCloud, nodeName string) (err error, ip string) {
// Generate Node Service
service := util.GenerateNodeService(instance, nodeName)
diff --git a/controllers/solrcloud_controller_backup_test.go b/controllers/solrcloud_controller_backup_test.go
index 55eef65..e70fe5c 100644
--- a/controllers/solrcloud_controller_backup_test.go
+++ b/controllers/solrcloud_controller_backup_test.go
@@ -75,6 +75,7 @@ var _ = FDescribe("SolrCloud controller - Backup Repositories", func() {
PodOptions: &solrv1beta1.PodOptions{
EnvVariables: extraVars,
Volumes: extraVolumes,
+ Annotations: testPodAnnotations,
},
},
BackupRepositories: []solrv1beta1.SolrBackupRepository{
@@ -98,7 +99,10 @@ var _ = FDescribe("SolrCloud controller - Backup Repositories", func() {
// Annotations for the solrxml configMap
solrXmlMd5 := fmt.Sprintf("%x", md5.Sum([]byte(configMap.Data[util.SolrXmlFile])))
- Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.SolrXmlMd5Annotation, solrXmlMd5), "Wrong solr.xml MD5 annotation in the pod template!")
+ Expect(statefulSet.Spec.Template.Annotations).To(Equal(util.MergeLabelsOrAnnotations(testPodAnnotations, map[string]string{
+ "solr.apache.org/solrXmlMd5": solrXmlMd5,
+ util.SolrBackupRepositoriesAnnotation: "test-repo",
+ })), "Incorrect pod annotations")
// Env Variable Tests
expectedEnvVars := map[string]string{
@@ -273,4 +277,52 @@ var _ = FDescribe("SolrCloud controller - Backup Repositories", func() {
})
})
})
+
+ FContext("Multiple Repositories - Annotations", func() {
+ BeforeEach(func() {
+ solrCloud.Spec = solrv1beta1.SolrCloudSpec{
+ ZookeeperRef: &solrv1beta1.ZookeeperRef{
+ ConnectionInfo: &solrv1beta1.ZookeeperConnectionInfo{
+ InternalConnectionString: "host:7271",
+ },
+ },
+ CustomSolrKubeOptions: solrv1beta1.CustomSolrKubeOptions{
+ PodOptions: &solrv1beta1.PodOptions{
+ EnvVariables: extraVars,
+ Volumes: extraVolumes,
+ },
+ },
+ BackupRepositories: []solrv1beta1.SolrBackupRepository{
+ {
+ Name: "test-repo",
+ S3: &solrv1beta1.S3Repository{
+ Region: "test-region",
+ Bucket: "test-bucket",
+ },
+ },
+ {
+ Name: "another",
+ S3: &solrv1beta1.S3Repository{
+ Region: "test-region-2",
+ Bucket: "test-bucket-2",
+ },
+ },
+ },
+ }
+ })
+ FIt("has the correct resources", func() {
+ By("testing the Solr ConfigMap")
+ configMap := expectConfigMap(ctx, solrCloud, solrCloud.ConfigMapName(), map[string]string{"solr.xml": util.GenerateSolrXMLStringForCloud(solrCloud)})
+
+ By("testing the Solr StatefulSet with explicit volumes and envVars before adding S3Repo credentials")
+ // Make sure envVars and Volumes are correct be
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+
+ // Annotations for the solrxml configMap
+ Expect(statefulSet.Spec.Template.Annotations).To(Equal(map[string]string{
+ "solr.apache.org/solrXmlMd5": fmt.Sprintf("%x", md5.Sum([]byte(configMap.Data["solr.xml"]))),
+ util.SolrBackupRepositoriesAnnotation: "another,test-repo",
+ }), "Incorrect pod annotations")
+ })
+ })
})
diff --git a/controllers/solrprometheusexporter_controller_test.go b/controllers/solrprometheusexporter_controller_test.go
index 7a6ae59..c9c8541 100644
--- a/controllers/solrprometheusexporter_controller_test.go
+++ b/controllers/solrprometheusexporter_controller_test.go
@@ -190,8 +190,9 @@ var _ = FDescribe("SolrPrometheusExporter controller - General", func() {
Expect(deployment.Labels).To(Equal(util.MergeLabelsOrAnnotations(expectedDeploymentLabels, testDeploymentLabels)), "Incorrect deployment labels")
Expect(deployment.Annotations).To(Equal(testDeploymentAnnotations), "Incorrect deployment annotations")
Expect(deployment.Spec.Template.ObjectMeta.Labels).To(Equal(util.MergeLabelsOrAnnotations(expectedDeploymentLabels, testPodLabels)), "Incorrect pod labels")
- testPodAnnotations[util.PrometheusExporterConfigXmlMd5Annotation] = fmt.Sprintf("%x", md5.Sum([]byte(testExporterConfig)))
- Expect(deployment.Spec.Template.ObjectMeta.Annotations).To(Equal(testPodAnnotations), "Incorrect pod annotations")
+ Expect(deployment.Spec.Template.ObjectMeta.Annotations).To(Equal(util.MergeLabelsOrAnnotations(testPodAnnotations, map[string]string{
+ util.PrometheusExporterConfigXmlMd5Annotation: fmt.Sprintf("%x", md5.Sum([]byte(testExporterConfig))),
+ })), "Incorrect pod annotations")
Expect(deployment.Spec.Template.Spec.Containers).To(HaveLen(len(extraContainers1)+1), "Wrong number of containers for the Deployment")
Expect(deployment.Spec.Template.Spec.Containers[1:]).To(Equal(extraContainers1), "Incorrect sidecar containers")
diff --git a/controllers/util/solr_api/cluster_status.go b/controllers/util/solr_api/cluster_status.go
index a1768e5..ac39bc7 100644
--- a/controllers/util/solr_api/cluster_status.go
+++ b/controllers/util/solr_api/cluster_status.go
@@ -17,73 +17,75 @@
package solr_api
+import "k8s.io/apimachinery/pkg/util/intstr"
+
type SolrOverseerStatusResponse struct {
ResponseHeader SolrResponseHeader `json:"responseHeader"`
// +optional
- Leader string `json:"leader"`
+ Leader string `json:"leader,omitempty"`
// +optional
- QueueSize int `json:"overseer_queue_size"`
+ QueueSize int `json:"overseer_queue_size,omitempty"`
// +optional
- WorkQueueSize int `json:"overseer_work_queue_size"`
+ WorkQueueSize int `json:"overseer_work_queue_size,omitempty"`
// +optional
- CollectionQueueSize int `json:"overseer_collection_queue_size"`
+ CollectionQueueSize int `json:"overseer_collection_queue_size,omitempty"`
}
type SolrClusterStatusResponse struct {
ResponseHeader SolrResponseHeader `json:"responseHeader"`
// +optional
- ClusterStatus SolrClusterStatus `json:"cluster"`
+ ClusterStatus SolrClusterStatus `json:"cluster,omitempty"`
}
type SolrClusterStatus struct {
// +optional
- Collections map[string]SolrCollectionStatus `json:"collections"`
+ Collections map[string]SolrCollectionStatus `json:"collections,omitempty"`
// +optional
- Aliases map[string]string `json:"aliases"`
+ Aliases map[string]string `json:"aliases,omitempty"`
// +optional
- Roles map[string][]string `json:"roles"`
+ Roles map[string][]string `json:"roles,omitempty"`
// +optional
- LiveNodes []string `json:"live_nodes"`
+ LiveNodes []string `json:"live_nodes,omitempty"`
}
type SolrCollectionStatus struct {
// +optional
- Shards map[string]SolrShardStatus `json:"shards"`
+ Shards map[string]SolrShardStatus `json:"shards,omitempty"`
// +optional
- ConfigName string `json:"configName"`
+ ConfigName string `json:"configName,omitempty"`
// +optional
- ZnodeVersion string `json:"znodeVersion"`
+ ZnodeVersion intstr.IntOrString `json:"znodeVersion,omitempty"`
// +optional
- AutoAddReplicas string `json:"autoAddReplicas"`
+ AutoAddReplicas string `json:"autoAddReplicas,omitempty"`
// +optional
- NrtReplicas int `json:"nrtReplicas"`
+ NrtReplicas intstr.IntOrString `json:"nrtReplicas,omitempty"`
// +optional
- TLogReplicas int `json:"tlogReplicas"`
+ TLogReplicas intstr.IntOrString `json:"tlogReplicas,omitempty"`
// +optional
- PullReplicas int `json:"pullReplicas"`
+ PullReplicas intstr.IntOrString `json:"pullReplicas,omitempty"`
// +optional
- MaxShardsPerNode string `json:"maxShardsPerNode"`
+ MaxShardsPerNode intstr.IntOrString `json:"maxShardsPerNode,omitempty"`
// +optional
- ReplicationFactor string `json:"replicationFactor"`
+ ReplicationFactor intstr.IntOrString `json:"replicationFactor,omitempty"`
// +optional
- Router SolrCollectionRouter `json:"router"`
+ Router SolrCollectionRouter `json:"router,omitempty"`
}
type SolrCollectionRouter struct {
@@ -92,13 +94,13 @@ type SolrCollectionRouter struct {
type SolrShardStatus struct {
// +optional
- Replicas map[string]SolrReplicaStatus `json:"replicas"`
+ Replicas map[string]SolrReplicaStatus `json:"replicas,omitempty"`
// +optional
- Range string `json:"range"`
+ Range string `json:"range,omitempty"`
// +optional
- State SolrShardState `json:"state"`
+ State SolrShardState `json:"state,omitempty"`
}
type SolrShardState string
@@ -120,7 +122,7 @@ type SolrReplicaStatus struct {
Leader bool `json:"leader,string"`
// +optional
- Type SolrReplicaType `json:"type"`
+ Type SolrReplicaType `json:"type,omitempty"`
}
type SolrReplicaState string
diff --git a/controllers/util/solr_backup_repo_util.go b/controllers/util/solr_backup_repo_util.go
index ced61cf..55f34f6 100644
--- a/controllers/util/solr_backup_repo_util.go
+++ b/controllers/util/solr_backup_repo_util.go
@@ -30,6 +30,8 @@ const (
GCSCredentialSecretKey = "service-account-key.json"
S3CredentialFileName = "credentials"
+
+ SolrBackupRepositoriesAnnotation = "solr.apache.org/backupRepositories"
)
func RepoVolumeName(repo *solrv1beta1.SolrBackupRepository) string {
@@ -229,3 +231,29 @@ func BackupLocationPath(repo *solrv1beta1.SolrBackupRepository, backupLocation s
}
return backupLocation
}
+
+func GetAvailableBackupRepos(pod *corev1.Pod) (repos map[string]bool) {
+ if availableRepos, hasAny := pod.Annotations[SolrBackupRepositoriesAnnotation]; hasAny {
+ repoNames := strings.Split(availableRepos, ",")
+ repos = make(map[string]bool, len(repoNames))
+ for _, repoName := range repoNames {
+ repos[repoName] = true
+ }
+ }
+ return
+}
+
+func SetAvailableBackupRepos(solrCloud *solrv1beta1.SolrCloud, podAnnotations map[string]string) map[string]string {
+ if len(solrCloud.Spec.BackupRepositories) > 0 {
+ if podAnnotations == nil {
+ podAnnotations = make(map[string]string, 1)
+ }
+ repoNames := make([]string, len(solrCloud.Spec.BackupRepositories))
+ for idx, repo := range solrCloud.Spec.BackupRepositories {
+ repoNames[idx] = repo.Name
+ }
+ sort.Strings(repoNames)
+ podAnnotations[SolrBackupRepositoriesAnnotation] = strings.Join(repoNames, ",")
+ }
+ return podAnnotations
+}
diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go
index 5b6b41b..9b122bc 100644
--- a/controllers/util/solr_util.go
+++ b/controllers/util/solr_util.go
@@ -211,6 +211,8 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCl
backupEnvVars = append(backupEnvVars, repoEnvVars...)
}
}
+ // Add annotation specifying the backupRepositories available with this version of the Pod.
+ podAnnotations = SetAvailableBackupRepos(solrCloud, podAnnotations)
if nil != customPodOptions {
// Add Custom Volumes to pod
diff --git a/example/test_backup_gcs.yaml b/example/test_backup_gcs.yaml
index f3e4ee3..778fabc 100644
--- a/example/test_backup_gcs.yaml
+++ b/example/test_backup_gcs.yaml
@@ -21,7 +21,7 @@ metadata:
name: main-collection-gcs-backups
namespace: default
spec:
- repositoryName: main_collection_backup_repository
- solrCloud: multiple-backup-repositories-solr
+ repositoryName: "main_collection_backup_repository"
+ solrCloud: multiple-backup-repos
collections:
- example
diff --git a/example/test_backup_managed.yaml b/example/test_backup_managed.yaml
index 3364c49..f66bd10 100644
--- a/example/test_backup_managed.yaml
+++ b/example/test_backup_managed.yaml
@@ -21,6 +21,6 @@ metadata:
namespace: default
spec:
repositoryName: "managed_repository_1"
- solrCloud: multiple-backup-repositories-solr
+ solrCloud: multiple-backup-repos
collections:
- example
diff --git a/example/test_solrcloud_backuprepos.yaml b/example/test_solrcloud_backuprepos.yaml
index 8de031c..6cb2922 100644
--- a/example/test_solrcloud_backuprepos.yaml
+++ b/example/test_solrcloud_backuprepos.yaml
@@ -16,7 +16,7 @@
apiVersion: solr.apache.org/v1beta1
kind: SolrCloud
metadata:
- name: multiple-backup-repositories-solr
+ name: multiple-backup-repos
spec:
replicas: 1
solrImage:
@@ -54,19 +54,18 @@ spec:
region: "us-west-2"
bucket: "product-catalog"
credentials:
- credentials:
- accessKeyIdSecret: # Optional
- name: aws-secrets
- key: access-key-id
- secretAccessKeySecret: # Optional
- name: aws-secrets
- key: secret-access-key
- sessionTokenSecret: # Optional
- name: aws-secrets
- key: session-token
- credentialsFileSecret: # Optional
- name: aws-credentials
- key: credentials
+ accessKeyIdSecret: # Optional
+ name: aws-secrets
+ key: access-key-id
+ secretAccessKeySecret: # Optional
+ name: aws-secrets
+ key: secret-access-key
+ sessionTokenSecret: # Optional
+ name: aws-secrets
+ key: session-token
+ credentialsFileSecret: # Optional
+ name: aws-credentials
+ key: credentials
- name: "main_collection_backup_repository_log"
gcs:
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index 4f9bd1b..257cb62 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -176,6 +176,13 @@ annotations:
url: https://github.com/apache/solr-operator/issues/352
- name: Github PR
url: https://github.com/apache/solr-operator/pull/361
+ - kind: added
+ description: Separate SolrCloud backup ready status by backup repository
+ links:
+ - name: Github Issue
+ url: https://github.com/apache/solr-operator/issues/326
+ - name: Github PR
+ url: https://github.com/apache/solr-operator/pull/358
artifacthub.io/images: |
- name: solr-operator
image: apache/solr-operator:v0.5.0-prerelease
diff --git a/helm/solr-operator/crds/crds.yaml b/helm/solr-operator/crds/crds.yaml
index a2d4ccd..6ff1bd1 100644
--- a/helm/solr-operator/crds/crds.yaml
+++ b/helm/solr-operator/crds/crds.yaml
@@ -1053,9 +1053,15 @@ spec:
type: object
repositoryName:
description: The name of the repository to use for the backup. Defaults to "legacy_local_repository" if not specified (the auto-configured repository for legacy singleton volumes).
+ maxLength: 100
+ minLength: 1
+ pattern: '[a-zA-Z0-9]([-_a-zA-Z0-9]*[a-zA-Z0-9])?'
type: string
solrCloud:
description: A reference to the SolrCloud to create a backup for
+ maxLength: 63
+ minLength: 1
+ pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?'
type: string
required:
- solrCloud
@@ -2155,6 +2161,9 @@ spec:
type: object
name:
description: 'A name used to identify this local storage profile. Values should follow RFC-1123. (See here for more details: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names)'
+ maxLength: 100
+ minLength: 1
+ pattern: '[a-zA-Z0-9]([-_a-zA-Z0-9]*[a-zA-Z0-9])?'
type: string
s3:
description: An S3Repository for Solr to use when backing up and restoring collections.
@@ -8061,6 +8070,11 @@ spec:
status:
description: SolrCloudStatus defines the observed state of SolrCloud
properties:
+ backupRepositoriesAvailable:
+ additionalProperties:
+ type: boolean
+ description: BackupRepositoriesAvailable lists the backupRepositories specified in the SolrCloud and whether they are available across all Pods.
+ type: object
backupRestoreReady:
description: BackupRestoreReady announces whether the solrCloud has the backupRestorePVC mounted to all pods and therefore is ready for backups and restores.
type: boolean
@@ -8074,11 +8088,11 @@ spec:
description: PodSelector for SolrCloud pods, required by the HPA
type: string
readyReplicas:
- description: ReadyReplicas is the number of number of ready replicas in the cluster
+ description: ReadyReplicas is the number of ready replicas in the cluster
format: int32
type: integer
replicas:
- description: Replicas is the number of number of desired replicas in the cluster
+ description: Replicas is the number of desired replicas in the cluster
format: int32
type: integer
solrNodes:
@@ -8120,7 +8134,7 @@ spec:
description: The version of solr that the cloud is meant to be running. Will only be provided when the cloud is migrating between versions
type: string
upToDateNodes:
- description: UpToDateNodes is the number of number of Solr Node pods that are running the latest pod spec
+ description: UpToDateNodes is the number of Solr Node pods that are running the latest pod spec
format: int32
type: integer
version: