sync all resources to cluster fields (#2713)

* sync all resources to cluster fields (CronJob, Streams, Patroni resources)
* separated sync and delete logic for Patroni resources
* align delete streams and secrets logic with other resources
* rename gatherApplicationIds to getDistinctApplicationIds
* improve slot check before syncing streams CRD
* add ownerReferences and annotations diff to Patroni objects
* add extra sync code for config service so it does not get too ugly
* some bugfixes when comparing annotations and return err on found
* sync Patroni resources on update event and extended unit tests
* add config service/endpoint owner references check to e2e tes
This commit is contained in:
Felix Kunde
2024-08-13 10:06:46 +02:00
committed by GitHub
parent 31f92a1aa0
commit 25ccc87317
11 changed files with 668 additions and 304 deletions
+118 -40
View File
@@ -41,10 +41,6 @@ var (
fesUser string = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix)
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{
TypeMeta: metav1.TypeMeta{
Kind: "Postgresql",
@@ -189,10 +185,95 @@ var (
func TestGatherApplicationIds(t *testing.T) {
testAppIds := []string{appId}
appIds := gatherApplicationIds(pg.Spec.Streams)
appIds := getDistinctApplicationIds(pg.Spec.Streams)
if !util.IsEqualIgnoreOrder(testAppIds, appIds) {
t.Errorf("gathered applicationIds do not match, expected %#v, got %#v", testAppIds, appIds)
t.Errorf("list of applicationIds does not match, expected %#v, got %#v", testAppIds, appIds)
}
}
func TestHasSlotsInSync(t *testing.T) {
tests := []struct {
subTest string
expectedSlots map[string]map[string]zalandov1.Slot
actualSlots map[string]map[string]string
slotsInSync bool
}{
{
subTest: "slots are in sync",
expectedSlots: map[string]map[string]zalandov1.Slot{
dbName: {
slotName: zalandov1.Slot{
Slot: map[string]string{
"databases": dbName,
"plugin": constants.EventStreamSourcePluginType,
"type": "logical",
},
Publication: map[string]acidv1.StreamTable{
"test1": acidv1.StreamTable{
EventType: "stream-type-a",
},
},
},
},
},
actualSlots: map[string]map[string]string{
slotName: map[string]string{
"databases": dbName,
"plugin": constants.EventStreamSourcePluginType,
"type": "logical",
},
},
slotsInSync: true,
}, {
subTest: "slots are not in sync",
expectedSlots: map[string]map[string]zalandov1.Slot{
dbName: {
slotName: zalandov1.Slot{
Slot: map[string]string{
"databases": dbName,
"plugin": constants.EventStreamSourcePluginType,
"type": "logical",
},
Publication: map[string]acidv1.StreamTable{
"test1": acidv1.StreamTable{
EventType: "stream-type-a",
},
},
},
},
"dbnotexists": {
slotName: zalandov1.Slot{
Slot: map[string]string{
"databases": "dbnotexists",
"plugin": constants.EventStreamSourcePluginType,
"type": "logical",
},
Publication: map[string]acidv1.StreamTable{
"test2": acidv1.StreamTable{
EventType: "stream-type-b",
},
},
},
},
},
actualSlots: map[string]map[string]string{
slotName: map[string]string{
"databases": dbName,
"plugin": constants.EventStreamSourcePluginType,
"type": "logical",
},
},
slotsInSync: false,
},
}
for _, tt := range tests {
result := hasSlotsInSync(appId, tt.expectedSlots, tt.actualSlots)
if !result {
t.Errorf("slots are not in sync, expected %#v, got %#v", tt.expectedSlots, tt.actualSlots)
}
}
}
@@ -226,7 +307,7 @@ func TestGenerateFabricEventStream(t *testing.T) {
assert.NoError(t, err)
// create the streams
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
err = cluster.syncStream(appId)
assert.NoError(t, err)
// compare generated stream with expected stream
@@ -252,7 +333,7 @@ func TestGenerateFabricEventStream(t *testing.T) {
}
// sync streams once again
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
err = cluster.syncStream(appId)
assert.NoError(t, err)
streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
@@ -401,7 +482,7 @@ func TestUpdateFabricEventStream(t *testing.T) {
assert.NoError(t, err)
// now create the stream
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
err = cluster.syncStream(appId)
assert.NoError(t, err)
// change specs of streams and patch CRD
@@ -415,46 +496,25 @@ func TestUpdateFabricEventStream(t *testing.T) {
}
}
patchData, err := specPatch(pg.Spec)
assert.NoError(t, err)
pgPatched, err := cluster.KubeClient.Postgresqls(namespace).Patch(
context.TODO(), cluster.Name, types.MergePatchType, patchData, metav1.PatchOptions{}, "spec")
assert.NoError(t, err)
cluster.Postgresql.Spec = pgPatched.Spec
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
assert.NoError(t, err)
// compare stream returned from API with expected stream
listOptions := metav1.ListOptions{
LabelSelector: cluster.labelsSet(true).String(),
}
streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
assert.NoError(t, err)
streams := patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions)
result := cluster.generateFabricEventStream(appId)
if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, result.Spec.EventStreams); !match {
t.Errorf("Malformed FabricEventStream after updating manifest, expected %#v, got %#v", streams.Items[0], result)
}
// disable recovery
for _, stream := range pg.Spec.Streams {
for idx, stream := range pg.Spec.Streams {
if stream.ApplicationId == appId {
stream.EnableRecovery = util.False()
pg.Spec.Streams[idx] = stream
}
}
patchData, err = specPatch(pg.Spec)
assert.NoError(t, err)
pgPatched, err = cluster.KubeClient.Postgresqls(namespace).Patch(
context.TODO(), cluster.Name, types.MergePatchType, patchData, metav1.PatchOptions{}, "spec")
assert.NoError(t, err)
cluster.Postgresql.Spec = pgPatched.Spec
err = cluster.createOrUpdateStreams(fakeCreatedSlots)
assert.NoError(t, err)
streams = patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions)
result = cluster.generateFabricEventStream(appId)
if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, result.Spec.EventStreams); !match {
t.Errorf("Malformed FabricEventStream after disabling event recovery, expected %#v, got %#v", streams.Items[0], result)
@@ -464,16 +524,34 @@ func TestUpdateFabricEventStream(t *testing.T) {
cluster.KubeClient.CustomResourceDefinitionsGetter = mockClient.CustomResourceDefinitionsGetter
// remove streams from manifest
pgPatched.Spec.Streams = nil
pg.Spec.Streams = nil
pgUpdated, err := cluster.KubeClient.Postgresqls(namespace).Update(
context.TODO(), pgPatched, metav1.UpdateOptions{})
context.TODO(), &pg, metav1.UpdateOptions{})
assert.NoError(t, err)
cluster.Postgresql.Spec = pgUpdated.Spec
cluster.createOrUpdateStreams(fakeCreatedSlots)
appIds := getDistinctApplicationIds(pgUpdated.Spec.Streams)
cluster.cleanupRemovedStreams(appIds)
streamList, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
if len(streamList.Items) > 0 || err != nil {
streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
if len(streams.Items) > 0 || err != nil {
t.Errorf("stream resource has not been removed or unexpected error %v", err)
}
}
func patchPostgresqlStreams(t *testing.T, cluster *Cluster, pgSpec *acidv1.PostgresSpec, listOptions metav1.ListOptions) (streams *zalandov1.FabricEventStreamList) {
patchData, err := specPatch(pgSpec)
assert.NoError(t, err)
pgPatched, err := cluster.KubeClient.Postgresqls(namespace).Patch(
context.TODO(), cluster.Name, types.MergePatchType, patchData, metav1.PatchOptions{}, "spec")
assert.NoError(t, err)
cluster.Postgresql.Spec = pgPatched.Spec
err = cluster.syncStream(appId)
assert.NoError(t, err)
streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions)
assert.NoError(t, err)
return streams
}