-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: WIP cleanup existing lockfiles on start and mark as failed
- Loading branch information
1 parent
cb52fd4
commit e6c3c23
Showing
9 changed files
with
224 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package storage | ||
|
||
import ( | ||
"os" | ||
|
||
"code.cloudfoundry.org/lager/v3" | ||
"github.com/cloudfoundry/cloud-service-broker/v2/dbservice/models" | ||
"gorm.io/gorm" | ||
) | ||
|
||
func (s *Storage) RecoverInProgressOperations(logger lager.Logger) error { | ||
logger = logger.Session("recover-in-progress-operations") | ||
|
||
// We only wan't to fail interrupted service instances if we detect that we run as a CF APP. | ||
// VM based csb instances implement a drain mechanism and should need this. Additionally VM | ||
// based csb deployments are scalable horizontally and the below would fail in flight instances | ||
// of another csb process. | ||
if os.Getenv("CF_INSTANCE_GUID") != "" { | ||
var terraformDeploymentBatch []models.TerraformDeployment | ||
result := s.db.Where("last_operation_state = ?", "in progress").FindInBatches(&terraformDeploymentBatch, 100, func(tx *gorm.DB, batchNumber int) error { | ||
for i := range terraformDeploymentBatch { | ||
terraformDeploymentBatch[i].LastOperationState = "failed" | ||
terraformDeploymentBatch[i].LastOperationMessage = "the broker restarted while the operation was in progress" | ||
logger.Info("mark-as-failed", lager.Data{"workspace_id": terraformDeploymentBatch[i].ID}) | ||
} | ||
|
||
return tx.Save(&terraformDeploymentBatch).Error | ||
}) | ||
|
||
return result.Error | ||
} else { | ||
deploymentIds, err := s.LockedDeploymentIds() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, id := range deploymentIds { | ||
var receiver models.TerraformDeployment | ||
if err := s.db.Where("id = ?", id).First(&receiver).Error; err != nil { | ||
return err | ||
} | ||
receiver.LastOperationState = "failed" | ||
err := s.db.Save(receiver).Error | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return err | ||
} | ||
|
||
} |
120 changes: 120 additions & 0 deletions
120
internal/storage/recover_in_progress_operations_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package storage_test | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"strings" | ||
|
||
"code.cloudfoundry.org/lager/v3/lagertest" | ||
"github.com/cloudfoundry/cloud-service-broker/v2/dbservice/models" | ||
"github.com/cloudfoundry/cloud-service-broker/v2/internal/storage" | ||
"github.com/cloudfoundry/cloud-service-broker/v2/internal/storage/storagefakes" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
"gorm.io/driver/sqlite" | ||
"gorm.io/gorm" | ||
) | ||
|
||
const ( | ||
recoverID = "fake-id-to-recover" | ||
okID = "fake-id-that-does-not-need-to-be-recovered" | ||
) | ||
|
||
var _ = Describe("RecoverInProgressOperations()", func() { | ||
BeforeEach(func() { | ||
|
||
// Setup | ||
db, err := gorm.Open(sqlite.Open(":memory:"), nil) | ||
Expect(err).NotTo(HaveOccurred()) | ||
Expect(db.Migrator().CreateTable(&models.TerraformDeployment{})).To(Succeed()) | ||
|
||
Expect(db.Create(&models.TerraformDeployment{ | ||
ID: recoverID, | ||
LastOperationType: "fake-type", | ||
LastOperationState: "in progress", | ||
LastOperationMessage: "fake-type in progress", | ||
}).Error).To(Succeed()) | ||
Expect(db.Create(&models.TerraformDeployment{ | ||
ID: okID, | ||
LastOperationType: "fake-type", | ||
LastOperationState: "succeeded", | ||
LastOperationMessage: "fake-type succeeded", | ||
}).Error).To(Succeed()) | ||
|
||
encryptor := &storagefakes.FakeEncryptor{ | ||
DecryptStub: func(bytes []byte) ([]byte, error) { | ||
if string(bytes) == `cannot-be-decrypted` { | ||
return nil, errors.New("fake decryption error") | ||
} | ||
return bytes, nil | ||
}, | ||
EncryptStub: func(bytes []byte) ([]byte, error) { | ||
if strings.Contains(string(bytes), `cannot-be-encrypted`) { | ||
return nil, errors.New("fake encryption error") | ||
} | ||
return []byte(`{"encrypted":` + string(bytes) + `}`), nil | ||
}, | ||
} | ||
|
||
logger = lagertest.NewTestLogger("test") | ||
store = storage.New(db, encryptor) | ||
}) | ||
|
||
When("running as a cf app", func() { | ||
It("recovers the expected operations", func() { | ||
os.Setenv("CF_INSTANCE_GUID", "something") // The presence of this variable means we are running as an App | ||
defer os.Unsetenv("CF_INSTANCE_GUID") | ||
|
||
// Call the function | ||
store.RecoverInProgressOperations(logger) | ||
|
||
// Behaviors | ||
By("marking the in-progress operation as failed") | ||
var r1 models.TerraformDeployment | ||
Expect(db.Where("id = ?", recoverID).First(&r1).Error).To(Succeed()) | ||
Expect(r1.LastOperationState).To(Equal("failed")) | ||
Expect(r1.LastOperationMessage).To(Equal("the broker restarted while the operation was in progress")) | ||
|
||
By("no updating other operations") | ||
var r2 models.TerraformDeployment | ||
Expect(db.Where("id = ?", okID).First(&r2).Error).To(Succeed()) | ||
Expect(r2.LastOperationState).To(Equal("succeeded")) | ||
Expect(r2.LastOperationMessage).To(Equal("fake-type succeeded")) | ||
|
||
By("logging the expected message") | ||
Expect(logger.Buffer().Contents()).To(SatisfyAll( | ||
ContainSubstring(`"message":"test.recover-in-progress-operations.mark-as-failed"`), | ||
ContainSubstring(`"workspace_id":"fake-id-to-recover"`), | ||
)) | ||
}) | ||
}) | ||
|
||
When("running on a VM", func() { | ||
It("recovers the expected operations", func() { | ||
// When running on a VM there will be a lockfile and record in the db | ||
store.WriteLockFile(recoverID) | ||
|
||
// Call the function | ||
store.RecoverInProgressOperations(logger) | ||
|
||
// Behaviors | ||
By("marking the in-progress operation as failed") | ||
var r1 models.TerraformDeployment | ||
Expect(db.Where("id = ?", recoverID).First(&r1).Error).To(Succeed()) | ||
Expect(r1.LastOperationState).To(Equal("failed")) | ||
Expect(r1.LastOperationMessage).To(Equal("the broker restarted while the operation was in progress")) | ||
|
||
By("no updating other operations") | ||
var r2 models.TerraformDeployment | ||
Expect(db.Where("id = ?", okID).First(&r2).Error).To(Succeed()) | ||
Expect(r2.LastOperationState).To(Equal("succeeded")) | ||
Expect(r2.LastOperationMessage).To(Equal("fake-type succeeded")) | ||
|
||
By("logging the expected message") | ||
Expect(logger.Buffer().Contents()).To(SatisfyAll( | ||
ContainSubstring(`"message":"test.recover-in-progress-operations.mark-as-failed"`), | ||
ContainSubstring(`"workspace_id":"fake-id-to-recover"`), | ||
)) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters