diff --git a/controllers/solrbackup_controller.go b/controllers/solrbackup_controller.go index 7d9c2444..c2bd2eef 100644 --- a/controllers/solrbackup_controller.go +++ b/controllers/solrbackup_controller.go @@ -311,7 +311,12 @@ func reconcileSolrCollectionBackup(ctx context.Context, backup *solrv1beta1.Solr if collectionBackupStatus.Successful == nil { collectionBackupStatus.Successful = &successful } - collectionBackupStatus.AsyncBackupStatus = "" + // Keep "notfound" so the status keeps saying why the backup could not be confirmed. + if asyncStatus == "notfound" { + collectionBackupStatus.AsyncBackupStatus = asyncStatus + } else { + collectionBackupStatus.AsyncBackupStatus = "" + } if collectionBackupStatus.FinishTime == nil { collectionBackupStatus.FinishTime = &now } diff --git a/controllers/util/backup_util.go b/controllers/util/backup_util.go index ca4056e1..a714a028 100644 --- a/controllers/util/backup_util.go +++ b/controllers/util/backup_util.go @@ -124,6 +124,12 @@ func CheckBackupForCollection(ctx context.Context, cloud *solr.SolrCloud, collec finished = true success = false } + if asyncStatus == "notfound" { + finished = true + success = false + logger.Info("Solr has no record of the collection backup, so it cannot be confirmed. Marking it unsuccessful; the backup repository may still contain a usable backup.", + "solrCloud", cloud.Name, "collection", collection) + } } else { logger.Error(err, "Error checking on collection backup", "solrCloud", cloud.Name, "collection", collection, "message", message) } diff --git a/controllers/util/backup_util_test.go b/controllers/util/backup_util_test.go index 691f8a3b..961cb620 100644 --- a/controllers/util/backup_util_test.go +++ b/controllers/util/backup_util_test.go @@ -18,13 +18,27 @@ package util import ( + "context" + "crypto/tls" + "fmt" solr "github.com/apache/solr-operator/api/v1beta1" + "github.com/apache/solr-operator/controllers/util/solr_api" + "github.com/go-logr/logr" "github.com/stretchr/testify/assert" + "io" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "net/http" + "strings" "testing" ) +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) { + return f(request) +} + func TestSolrBackupApiParamsForVolumeRepositoryBackup(t *testing.T) { volumeRepository := &solr.SolrBackupRepository{ Name: "some-volume-repository", @@ -413,3 +427,51 @@ func TestRepositoryLookupFailsIfNoNameProvidedAndMultipleRepositoriesDefined(t * assert.Nil(t, found, "Expected GetBackupRepositoryByName to report no match") } + +// A backup whose async status Solr no longer holds must be terminal, otherwise it is checked +// forever. It is unsuccessful because success cannot be shown, not because failure is known. +func TestCheckBackupForCollectionOutcome(t *testing.T) { + tests := map[string]struct { + finished bool + success bool + }{ + "completed": {finished: true, success: true}, + "failed": {finished: true, success: false}, + "notfound": {finished: true, success: false}, + "running": {finished: false, success: false}, + "submitted": {finished: false, success: false}, + } + + defer func() { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + solr_api.SetNoVerifyTLSHttpClient(&http.Client{Transport: transport}) + }() + + for asyncStatus, expected := range tests { + solr_api.SetNoVerifyTLSHttpClient(&http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) { + assert.Equal(t, "REQUESTSTATUS", request.URL.Query().Get("action")) + assert.Equal(t, "backup-collection", request.URL.Query().Get("requestid")) + return &http.Response{ + StatusCode: http.StatusOK, + Header: make(http.Header), + Body: io.NopCloser(strings.NewReader(fmt.Sprintf( + `{"responseHeader":{"status":0},"status":{"state":%q}}`, asyncStatus, + ))), + }, nil + })}) + + finished, success, actualStatus, err := CheckBackupForCollection( + context.Background(), + &solr.SolrCloud{ObjectMeta: metav1.ObjectMeta{Name: "cloud", Namespace: "namespace"}}, + "collection", + "backup", + logr.Discard(), + ) + + assert.NoError(t, err) + assert.Equal(t, asyncStatus, actualStatus) + assert.Equal(t, expected.finished, finished, "Wrong finished for async status %q", asyncStatus) + assert.Equal(t, expected.success, success, "Wrong success for async status %q", asyncStatus) + } +}