stream: slot and FES should not be created if the publication creation fails (#2704)
* slot should not be created if the publication creation fails * not create FES resource when slot doesn't exist
This commit is contained in:
parent
31f474a95c
commit
94d36327ba
|
|
@ -2041,6 +2041,20 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
"recoveryEventType": "test-event-dlq"
|
"recoveryEventType": "test-event-dlq"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"applicationId": "test-app2",
|
||||||
|
"batchSize": 100,
|
||||||
|
"database": "foo",
|
||||||
|
"enableRecovery": True,
|
||||||
|
"tables": {
|
||||||
|
"test_non_exist_table": {
|
||||||
|
"eventType": "test-event",
|
||||||
|
"idColumn": "id",
|
||||||
|
"payloadColumn": "payload",
|
||||||
|
"recoveryEventType": "test-event-dlq"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -2064,6 +2078,18 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
"zalando.org", "v1", "default", "fabriceventstreams", label_selector="cluster-name=acid-minimal-cluster")["items"]), 1,
|
"zalando.org", "v1", "default", "fabriceventstreams", label_selector="cluster-name=acid-minimal-cluster")["items"]), 1,
|
||||||
"Could not find Fabric Event Stream resource", 10, 5)
|
"Could not find Fabric Event Stream resource", 10, 5)
|
||||||
|
|
||||||
|
# check if the non-existing table in the stream section does not create a publication and slot
|
||||||
|
get_publication_query_not_exist_table = """
|
||||||
|
SELECT * FROM pg_publication WHERE pubname = 'fes_foo_test_app2';
|
||||||
|
"""
|
||||||
|
get_slot_query_not_exist_table = """
|
||||||
|
SELECT * FROM pg_replication_slots WHERE slot_name = 'fes_foo_test_app2';
|
||||||
|
"""
|
||||||
|
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "foo", get_publication_query_not_exist_table)), 0,
|
||||||
|
"Publication is created for non-existing tables", 10, 5)
|
||||||
|
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "foo", get_slot_query_not_exist_table)), 0,
|
||||||
|
"Replication slot is created for non-existing tables", 10, 5)
|
||||||
|
|
||||||
# grant create and ownership of test_table to foo_user, reset search path to default
|
# grant create and ownership of test_table to foo_user, reset search path to default
|
||||||
grant_permission_foo_user = """
|
grant_permission_foo_user = """
|
||||||
GRANT CREATE ON DATABASE foo TO foo_user;
|
GRANT CREATE ON DATABASE foo TO foo_user;
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,6 @@ func (c *Cluster) syncPublication(dbName string, databaseSlotsList map[string]za
|
||||||
} else if currentTables != tableList {
|
} else if currentTables != tableList {
|
||||||
alterPublications[slotName] = tableList
|
alterPublications[slotName] = tableList
|
||||||
}
|
}
|
||||||
(*slotsToSync)[slotName] = slotAndPublication.Slot
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if there is any deletion
|
// check if there is any deletion
|
||||||
|
|
@ -148,24 +147,30 @@ func (c *Cluster) syncPublication(dbName string, databaseSlotsList map[string]za
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errorMessage error = nil
|
||||||
for publicationName, tables := range createPublications {
|
for publicationName, tables := range createPublications {
|
||||||
if err = c.executeCreatePublication(publicationName, tables); err != nil {
|
if err = c.executeCreatePublication(publicationName, tables); err != nil {
|
||||||
return fmt.Errorf("creation of publication %q failed: %v", publicationName, err)
|
errorMessage = fmt.Errorf("creation of publication %q failed: %v", publicationName, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
(*slotsToSync)[publicationName] = databaseSlotsList[publicationName].Slot
|
||||||
}
|
}
|
||||||
for publicationName, tables := range alterPublications {
|
for publicationName, tables := range alterPublications {
|
||||||
if err = c.executeAlterPublication(publicationName, tables); err != nil {
|
if err = c.executeAlterPublication(publicationName, tables); err != nil {
|
||||||
return fmt.Errorf("update of publication %q failed: %v", publicationName, err)
|
errorMessage = fmt.Errorf("update of publication %q failed: %v", publicationName, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
(*slotsToSync)[publicationName] = databaseSlotsList[publicationName].Slot
|
||||||
}
|
}
|
||||||
for _, publicationName := range deletePublications {
|
for _, publicationName := range deletePublications {
|
||||||
(*slotsToSync)[publicationName] = nil
|
|
||||||
if err = c.executeDropPublication(publicationName); err != nil {
|
if err = c.executeDropPublication(publicationName); err != nil {
|
||||||
return fmt.Errorf("deletion of publication %q failed: %v", publicationName, err)
|
errorMessage = fmt.Errorf("deletion of publication %q failed: %v", publicationName, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
(*slotsToSync)[publicationName] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errorMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEventStream {
|
func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEventStream {
|
||||||
|
|
@ -390,7 +395,7 @@ func (c *Cluster) syncStreams() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally sync stream CRDs
|
// finally sync stream CRDs
|
||||||
err = c.createOrUpdateStreams()
|
err = c.createOrUpdateStreams(slotsToSync)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -398,7 +403,7 @@ func (c *Cluster) syncStreams() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) createOrUpdateStreams() error {
|
func (c *Cluster) createOrUpdateStreams(createdSlots map[string]map[string]string) error {
|
||||||
|
|
||||||
// fetch different application IDs from streams section
|
// fetch different application IDs from streams section
|
||||||
// there will be a separate event stream resource for each ID
|
// there will be a separate event stream resource for each ID
|
||||||
|
|
@ -413,7 +418,7 @@ func (c *Cluster) createOrUpdateStreams() error {
|
||||||
return fmt.Errorf("could not list of FabricEventStreams: %v", err)
|
return fmt.Errorf("could not list of FabricEventStreams: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, appId := range appIds {
|
for idx, appId := range appIds {
|
||||||
streamExists := false
|
streamExists := false
|
||||||
|
|
||||||
// update stream when it exists and EventStreams array differs
|
// update stream when it exists and EventStreams array differs
|
||||||
|
|
@ -435,6 +440,12 @@ func (c *Cluster) createOrUpdateStreams() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !streamExists {
|
if !streamExists {
|
||||||
|
// check if there is any slot with the applicationId
|
||||||
|
slotName := getSlotName(c.Spec.Streams[idx].Database, appId)
|
||||||
|
if _, exists := createdSlots[slotName]; !exists {
|
||||||
|
c.logger.Warningf("no slot %s with applicationId %s exists, skipping event stream creation", slotName, appId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
c.logger.Infof("event streams with applicationId %s do not exist, create it", appId)
|
c.logger.Infof("event streams with applicationId %s do not exist, create it", appId)
|
||||||
streamCRD, err := c.createStreams(appId)
|
streamCRD, err := c.createStreams(appId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ var (
|
||||||
fesUser string = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix)
|
fesUser string = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix)
|
||||||
slotName string = fmt.Sprintf("%s_%s_%s", constants.EventStreamSourceSlotPrefix, dbName, strings.Replace(appId, "-", "_", -1))
|
slotName string = fmt.Sprintf("%s_%s_%s", constants.EventStreamSourceSlotPrefix, dbName, strings.Replace(appId, "-", "_", -1))
|
||||||
|
|
||||||
|
fakeCreatedSlots map[string]map[string]string = map[string]map[string]string{
|
||||||
|
slotName: {},
|
||||||
|
}
|
||||||
|
|
||||||
pg = acidv1.Postgresql{
|
pg = acidv1.Postgresql{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
Kind: "Postgresql",
|
Kind: "Postgresql",
|
||||||
|
|
@ -222,7 +226,7 @@ func TestGenerateFabricEventStream(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// create the streams
|
// create the streams
|
||||||
err = cluster.createOrUpdateStreams()
|
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// compare generated stream with expected stream
|
// compare generated stream with expected stream
|
||||||
|
|
@ -248,7 +252,7 @@ func TestGenerateFabricEventStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync streams once again
|
// sync streams once again
|
||||||
err = cluster.createOrUpdateStreams()
|
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
|
streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
|
||||||
|
|
@ -397,7 +401,7 @@ func TestUpdateFabricEventStream(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// now create the stream
|
// now create the stream
|
||||||
err = cluster.createOrUpdateStreams()
|
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// change specs of streams and patch CRD
|
// change specs of streams and patch CRD
|
||||||
|
|
@ -419,7 +423,7 @@ func TestUpdateFabricEventStream(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
cluster.Postgresql.Spec = pgPatched.Spec
|
cluster.Postgresql.Spec = pgPatched.Spec
|
||||||
err = cluster.createOrUpdateStreams()
|
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// compare stream returned from API with expected stream
|
// compare stream returned from API with expected stream
|
||||||
|
|
@ -448,7 +452,7 @@ func TestUpdateFabricEventStream(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
cluster.Postgresql.Spec = pgPatched.Spec
|
cluster.Postgresql.Spec = pgPatched.Spec
|
||||||
err = cluster.createOrUpdateStreams()
|
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
result = cluster.generateFabricEventStream(appId)
|
result = cluster.generateFabricEventStream(appId)
|
||||||
|
|
@ -466,7 +470,7 @@ func TestUpdateFabricEventStream(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
cluster.Postgresql.Spec = pgUpdated.Spec
|
cluster.Postgresql.Spec = pgUpdated.Spec
|
||||||
cluster.createOrUpdateStreams()
|
cluster.createOrUpdateStreams(fakeCreatedSlots)
|
||||||
|
|
||||||
streamList, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
|
streamList, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
|
||||||
if len(streamList.Items) > 0 || err != nil {
|
if len(streamList.Items) > 0 || err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue