Skip to content

Commit

Permalink
Add support for S3Repositories (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
HoustonPutman authored Oct 18, 2021
1 parent 125a2f2 commit 3f851ac
Show file tree
Hide file tree
Showing 20 changed files with 1,108 additions and 70 deletions.
13 changes: 11 additions & 2 deletions api/v1beta1/solrbackup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ type SolrBackupSpec struct {
// +optional
Collections []string `json:"collections,omitempty"`

// The location to store the backup in the specified backup repository.
// +optional
Location string `json:"location,omitempty"`

// Persistence is the specification on how to persist the backup data.
// +optional
Persistence *PersistenceSource `json:"persistence,omitempty"`
Expand Down Expand Up @@ -202,7 +206,8 @@ type SolrBackupStatus struct {
CollectionBackupStatuses []CollectionBackupStatus `json:"collectionBackupStatuses,omitempty"`

// Whether the backups are in progress of being persisted
PersistenceStatus BackupPersistenceStatus `json:"persistenceStatus"`
// +optional
PersistenceStatus *BackupPersistenceStatus `json:"persistenceStatus,omitempty"`

// Version of the Solr being backed up
// +optional
Expand All @@ -221,6 +226,10 @@ type CollectionBackupStatus struct {
// Solr Collection name
Collection string `json:"collection"`

// BackupName of this collection's backup in Solr
// +optional
BackupName string `json:"backupName,omitempty"`

// Whether the collection is being backed up
// +optional
InProgress bool `json:"inProgress,omitempty"`
Expand Down Expand Up @@ -284,7 +293,7 @@ func (sb *SolrBackup) SharedLabelsWith(labels map[string]string) map[string]stri
return newLabels
}

// HeadlessServiceName returns the name of the headless service for the cloud
// PersistenceJobName returns the name of the persistence job for the backup
func (sb *SolrBackup) PersistenceJobName() string {
return fmt.Sprintf("%s-solr-backup-persistence", sb.GetName())
}
Expand Down
52 changes: 52 additions & 0 deletions api/v1beta1/solrcloud_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@ type SolrBackupRepository struct {
//+optional
GCS *GcsRepository `json:"gcs,omitempty"`

// An S3Repository for Solr to use when backing up and restoring collections.
//+optional
S3 *S3Repository `json:"s3,omitempty"`

// Allows specification of a "repository" for Solr to use when backing up data "locally".
// Repositories defined here are considered "managed" and can take advantage of special operator features, such as
// post-backup compression.
Expand All @@ -410,6 +414,54 @@ type GcsRepository struct {
BaseLocation string `json:"baseLocation,omitempty"`
}

type S3Repository struct {
// The S3 region to store the backup data in
Region string `json:"region"`

// The name of the S3 bucket that all backup data will be stored in
Bucket string `json:"bucket"`

// Options for specifying S3Credentials. This is optional in case you want to mount this information yourself.
// However, if you do not include these credentials, and you do not load them yourself via a mount or EnvVars,
// you will likely see errors when taking s3 backups.
//
// If running in EKS, you can create an IAMServiceAccount that uses a role permissioned for this S3 bucket.
// Then use that serviceAccountName for your SolrCloud, and the credentials should be auto-populated.
//
// +optional
Credentials *S3Credentials `json:"credentials,omitempty"`

// An already-created chroot within the bucket to store data in. Defaults to the root path "/" if not specified.
// +optional
BaseLocation string `json:"baseLocation,omitempty"`

// The full endpoint URL to use when connecting with S3 (or a supported S3 compatible interface)
// +optional
Endpoint string `json:"endpoint,omitempty"`

// The full proxy URL to use when connecting with S3
// +optional
ProxyUrl string `json:"proxyUrl,omitempty"`
}

type S3Credentials struct {
// The name & key of a Kubernetes secret holding an AWS Access Key ID
// +optional
AccessKeyIdSecret *corev1.SecretKeySelector `json:"accessKeyIdSecret,omitempty"`

// The name & key of a Kubernetes secret holding an AWS Secret Access Key
// +optional
SecretAccessKeySecret *corev1.SecretKeySelector `json:"secretAccessKeySecret,omitempty"`

// The name & key of a Kubernetes secret holding an AWS Session Token
// +optional
SessionTokenSecret *corev1.SecretKeySelector `json:"sessionTokenSecret,omitempty"`

// The name & key of a Kubernetes secret holding an AWS credentials file
// +optional
CredentialsFileSecret *corev1.SecretKeySelector `json:"credentialsFileSecret,omitempty"`
}

type ManagedRepository struct {
// This is a volumeSource for a volume that will be mounted to all solrNodes to store backups and load restores.
// The data within the volume will be namespaced for this instance, so feel free to use the same volume for multiple clouds.
Expand Down
66 changes: 65 additions & 1 deletion api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion config/crd/bases/solr.apache.org_solrbackups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ spec:
items:
type: string
type: array
location:
description: The location to store the backup in the specified backup repository.
type: string
persistence:
description: Persistence is the specification on how to persist the backup data.
properties:
Expand Down Expand Up @@ -1068,6 +1071,9 @@ spec:
asyncBackupStatus:
description: The status of the asynchronous backup call to solr
type: string
backupName:
description: BackupName of this collection's backup in Solr
type: string
collection:
description: Solr Collection name
type: string
Expand Down Expand Up @@ -1127,7 +1133,6 @@ spec:
description: Whether the backup was successful
type: boolean
required:
- persistenceStatus
- solrVersion
type: object
type: object
Expand Down
86 changes: 86 additions & 0 deletions config/crd/bases/solr.apache.org_solrclouds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,92 @@ spec:
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)'
type: string
s3:
description: An S3Repository for Solr to use when backing up and restoring collections.
properties:
baseLocation:
description: An already-created chroot within the bucket to store data in. Defaults to the root path "/" if not specified.
type: string
bucket:
description: The name of the S3 bucket that all backup data will be stored in
type: string
credentials:
description: "Options for specifying S3Credentials. This is optional in case you want to mount this information yourself. However, if you do not include these credentials, and you do not load them yourself via a mount or EnvVars, you will likely see errors when taking s3 backups. \n If running in EKS, you can create an IAMServiceAccount that uses a role permissioned for this S3 bucket. Then use that serviceAccountName for your SolrCloud, and the credentials should be auto-populated."
properties:
accessKeyIdSecret:
description: The name & key of a Kubernetes secret holding an AWS Access Key ID
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
credentialsFileSecret:
description: The name & key of a Kubernetes secret holding an AWS credentials file
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
secretAccessKeySecret:
description: The name & key of a Kubernetes secret holding an AWS Secret Access Key
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
sessionTokenSecret:
description: The name & key of a Kubernetes secret holding an AWS Session Token
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
type: object
endpoint:
description: The full endpoint URL to use when connecting with S3 (or a supported S3 compatible interface)
type: string
proxyUrl:
description: The full proxy URL to use when connecting with S3
type: string
region:
description: The S3 region to store the backup data in
type: string
required:
- bucket
- region
type: object
required:
- name
type: object
Expand Down
21 changes: 14 additions & 7 deletions controllers/solrbackup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,17 @@ func (r *SolrBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request)
if backup.Status.Finished && backup.Status.FinishTime == nil {
now := metav1.Now()
backup.Status.FinishTime = &now
backup.Status.Successful = backup.Status.PersistenceStatus.Successful
if backup.Spec.Persistence != nil {
backup.Status.Successful = backup.Status.PersistenceStatus.Successful
}
}

if !reflect.DeepEqual(oldStatus, backup.Status) {
if !reflect.DeepEqual(oldStatus, &backup.Status) {
logger.Info("Updating status for solr-backup")
err = r.Status().Update(ctx, backup)
}

if backup.Status.Finished {
if err != nil && backup.Status.Finished {
requeueOrNot = reconcile.Result{}
}

Expand Down Expand Up @@ -187,12 +189,13 @@ func (r *SolrBackupReconciler) reconcileSolrCloudBackup(ctx context.Context, bac
// This should only occur before the backup processes have been started
if backup.Status.SolrVersion == "" {
// Prep the backup directory in the persistentVolume
err := util.EnsureDirectoryForBackup(solrCloud, backupRepository, backup.Name, r.config)
err = util.EnsureDirectoryForBackup(solrCloud, backupRepository, backup, r.config)
if err != nil {
return solrCloud, collectionBackupsFinished, 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)
Expand Down Expand Up @@ -238,11 +241,12 @@ func reconcileSolrCollectionBackup(backup *solrv1beta1.SolrBackup, solrCloud *so
if started && collectionBackupStatus.StartTime == nil {
collectionBackupStatus.StartTime = &now
}
collectionBackupStatus.BackupName = util.FullCollectionBackupName(collection, backup.Name)
} else if collectionBackupStatus.InProgress {
// Check the state of the backup, when it is in progress, and update the state accordingly
finished, successful, asyncStatus, error := util.CheckBackupForCollection(solrCloud, collection, backup.Name, httpHeaders, logger)
if error != nil {
return false, error
finished, successful, asyncStatus, err := util.CheckBackupForCollection(solrCloud, collection, backup.Name, httpHeaders, logger)
if err != nil {
return false, err
}
collectionBackupStatus.Finished = finished
if finished {
Expand Down Expand Up @@ -271,6 +275,9 @@ func reconcileSolrCollectionBackup(backup *solrv1beta1.SolrBackup, solrCloud *so
}

func (r *SolrBackupReconciler) persistSolrCloudBackups(ctx context.Context, backup *solrv1beta1.SolrBackup, solrCloud *solrv1beta1.SolrCloud, logger logr.Logger) (err error) {
if backup.Status.PersistenceStatus == nil {
backup.Status.PersistenceStatus = &solrv1beta1.BackupPersistenceStatus{}
}
if backup.Status.PersistenceStatus.Finished {
return nil
}
Expand Down
Loading

0 comments on commit 3f851ac

Please sign in to comment.