Skip to content

Commit e1edfa0

Browse files
committed
test(versioning): end-to-end rollback drill
1 parent 43567af commit e1edfa0

1 file changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package versioning
19+
20+
import (
21+
"context"
22+
"testing"
23+
"time"
24+
25+
"github.com/stretchr/testify/require"
26+
"k8s.io/client-go/tools/cache"
27+
28+
meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
29+
"github.com/apache/dubbo-admin/pkg/core/events"
30+
meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
31+
)
32+
33+
func TestE2ERollbackDrill(t *testing.T) {
34+
store := NewMemoryStore()
35+
hints := NewAdminHintRegistry()
36+
maxVersions := int64(5)
37+
svc := NewServiceWithRollbackWait(true, maxVersions, 0, 30*time.Second, time.Second, store, hints)
38+
sub := NewSubscriber(meshresource.ConditionRouteKind, store, hints, maxVersions, 0)
39+
bus := newTestEventBus(t)
40+
defer bus.WaitForDone()
41+
require.NoError(t, bus.Subscribe(sub))
42+
require.NoError(t, bus.Start(nil, nil))
43+
44+
original := newE2EConditionRoute(1)
45+
require.NoError(t, RecordBootstrap(store, maxVersions, original))
46+
items := requireVersions(t, store, original.ResourceKey(), 1)
47+
require.Equal(t, SourceBootstrap, items[0].Source)
48+
require.Equal(t, OperationCreate, items[0].Operation)
49+
require.Equal(t, int64(1), items[0].VersionNo)
50+
require.Equal(t, "system:bootstrap", items[0].Author)
51+
bootstrapID := items[0].ID
52+
53+
adminEdit := newE2EConditionRoute(2)
54+
require.NoError(t, svc.PutAdminHint(adminEdit, OperationUpdate, SourceAdmin, "alice", "raise priority", nil))
55+
bus.Send(events.NewResourceChangedEvent(cache.Updated, original, adminEdit))
56+
items = requireVersions(t, store, original.ResourceKey(), 2)
57+
require.Equal(t, SourceAdmin, items[0].Source)
58+
require.Equal(t, "alice", items[0].Author)
59+
require.Equal(t, int64(2), items[0].VersionNo)
60+
61+
upstreamPush := newE2EConditionRoute(3)
62+
bus.Send(events.NewResourceChangedEventWithContext(cache.Updated, adminEdit, upstreamPush, map[string]string{
63+
"source-registry": "zookeeper",
64+
}))
65+
items = requireVersions(t, store, original.ResourceKey(), 3)
66+
require.Equal(t, SourceUpstream, items[0].Source)
67+
require.Equal(t, "system:zookeeper", items[0].Author)
68+
require.Equal(t, int64(3), items[0].VersionNo)
69+
70+
currentID := items[0].ID
71+
rollback, err := svc.Rollback(
72+
context.Background(),
73+
eventBusVersionResourceManager{emitter: bus},
74+
meshresource.ConditionRouteKind,
75+
"mesh",
76+
"demo.condition-router",
77+
bootstrapID,
78+
"restore bootstrap baseline",
79+
&currentID,
80+
"bob",
81+
)
82+
require.NoError(t, err)
83+
require.Equal(t, SourceRollback, rollback.Source)
84+
require.Equal(t, OperationUpdate, rollback.Operation)
85+
require.NotNil(t, rollback.RolledBackFromID)
86+
require.Equal(t, bootstrapID, *rollback.RolledBackFromID)
87+
require.Equal(t, "bob", rollback.Author)
88+
require.Equal(t, int64(4), rollback.VersionNo)
89+
90+
items = requireVersions(t, store, original.ResourceKey(), 4)
91+
requireAuditChainReadable(t, items, []Source{SourceRollback, SourceUpstream, SourceAdmin, SourceBootstrap})
92+
93+
previous := newE2EConditionRoute(1)
94+
for priority := int32(4); priority <= 9; priority++ {
95+
next := newE2EConditionRoute(priority)
96+
require.NoError(t, svc.PutAdminHint(next, OperationUpdate, SourceAdmin, "alice", "bulk edit", nil))
97+
bus.Send(events.NewResourceChangedEvent(cache.Updated, previous, next))
98+
previous = next
99+
require.Eventually(t, func() bool {
100+
latest, err := store.LatestVersion(meshresource.ConditionRouteKind, original.ResourceKey())
101+
return err == nil && latest != nil && latest.VersionNo == int64(priority+1)
102+
}, time.Second, 10*time.Millisecond)
103+
}
104+
105+
items = requireVersions(t, store, original.ResourceKey(), int(maxVersions))
106+
require.Equal(t, []int64{10, 9, 8, 7, 6}, versionNumbers(items))
107+
for _, item := range items {
108+
require.NotEmpty(t, item.Author)
109+
require.False(t, item.CreatedAt.IsZero())
110+
}
111+
}
112+
113+
func newE2EConditionRoute(priority int32) *meshresource.ConditionRouteResource {
114+
res := meshresource.NewConditionRouteResourceWithAttributes("demo.condition-router", "mesh")
115+
res.Spec = &meshproto.ConditionRoute{
116+
Key: "demo",
117+
Enabled: true,
118+
Priority: priority,
119+
Conditions: []string{"host = 127.0.0.1"},
120+
}
121+
return res
122+
}
123+
124+
func requireVersions(t *testing.T, store Store, resourceKey string, count int) []Version {
125+
t.Helper()
126+
var items []Version
127+
require.Eventually(t, func() bool {
128+
var err error
129+
items, err = store.ListVersions(meshresource.ConditionRouteKind, resourceKey)
130+
return err == nil && len(items) == count
131+
}, time.Second, 10*time.Millisecond)
132+
return items
133+
}
134+
135+
func requireAuditChainReadable(t *testing.T, items []Version, sources []Source) {
136+
t.Helper()
137+
require.Len(t, items, len(sources))
138+
for i, item := range items {
139+
require.Equal(t, sources[i], item.Source)
140+
require.NotEmpty(t, item.Author)
141+
require.False(t, item.CreatedAt.IsZero())
142+
if i > 0 {
143+
require.False(t, items[i-1].CreatedAt.Before(item.CreatedAt))
144+
}
145+
}
146+
}
147+
148+
func versionNumbers(items []Version) []int64 {
149+
numbers := make([]int64, 0, len(items))
150+
for _, item := range items {
151+
numbers = append(numbers, item.VersionNo)
152+
}
153+
return numbers
154+
}

0 commit comments

Comments
 (0)