1 package edgedb
2
3 import (
4 "context"
5 "fmt"
6 "testing"
7 "time"
8
9 "github.com/google/uuid"
10
11 "edge-infra.dev/pkg/edge/api/testutils/seededpostgres"
12 bannerApi "edge-infra.dev/pkg/edge/apis/banner/v1alpha1"
13 clusterApi "edge-infra.dev/pkg/edge/apis/cluster/v1alpha1"
14 gkeclusterApi "edge-infra.dev/pkg/edge/apis/gkecluster/v1alpha1"
15 "edge-infra.dev/pkg/edge/controllers/dbmetrics"
16 "edge-infra.dev/pkg/k8s/runtime/conditions"
17 "edge-infra.dev/pkg/lib/build/bazel"
18
19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20 )
21
22 var edb *EdgeDB
23
24 func TestRecordInfraStatus(t *testing.T) {
25 if !bazel.IsBazelTest() && !bazel.IsBazelRun() {
26 t.Skip()
27 return
28 }
29
30 sp, err := seededpostgres.New()
31 if err != nil {
32 t.Fatal(err)
33 }
34 db, err := sp.DB()
35 if err != nil {
36 t.Fatal(err)
37 }
38
39 edb = &EdgeDB{DB: db}
40
41 defer func() {
42 _ = db.Close()
43 _ = sp.Close()
44 }()
45
46 t.Run("banner", testStatusForBannerAPI)
47 t.Run("cluster", testStatusForClusterAPI)
48 t.Run("gkecluster", testStatusForGKEClusterAPI)
49 t.Run("store", testStoreClusterArtifacts)
50 }
51
52
53 func testStatusForBannerAPI(t *testing.T) {
54
55 var bannerEdgeID string
56
57 row := edb.DB.QueryRow("SELECT banner_edge_id FROM banners LIMIT 1")
58 if err := row.Err(); err != nil {
59 t.Fatal(err)
60 } else if err = row.Scan(&bannerEdgeID); err != nil {
61 t.Fatal(err)
62 }
63
64 var now = time.Now().UTC().Round(time.Second)
65 var banner = &bannerApi.Banner{
66 TypeMeta: metav1.TypeMeta{
67 Kind: bannerApi.BannerKind,
68 },
69 ObjectMeta: metav1.ObjectMeta{
70 Name: bannerEdgeID,
71 },
72 Status: bannerApi.BannerStatus{
73 Conditions: make([]metav1.Condition, 1),
74 },
75 }
76
77
78 banner.Status.Conditions[0] = metav1.Condition{
79 Type: "Ready",
80 Status: metav1.ConditionTrue,
81 Message: "banner reconciled successfully",
82 Reason: bannerApi.ProvisionSucceededReason,
83 ObservedGeneration: 1,
84 LastTransitionTime: metav1.Time{Time: now},
85 }
86
87 edb.RecordInfraStatus(context.Background(), banner, dbmetrics.DBMetrics{})
88 validateExpectedInfraStatus(t, banner)
89
90
91 banner.Status.Conditions[0] = metav1.Condition{
92 Type: "Ready",
93 Status: metav1.ConditionFalse,
94 Message: "oof",
95 Reason: "OofReason",
96 ObservedGeneration: 1,
97 LastTransitionTime: metav1.Time{Time: now},
98 }
99
100 edb.RecordInfraStatus(context.Background(), banner, dbmetrics.DBMetrics{})
101 validateExpectedInfraStatus(t, banner)
102 }
103
104
105 func testStatusForGKEClusterAPI(t *testing.T) {
106 var (
107 clusterEdgeID = uuid.NewString()
108 clusterName = "gkeclusterFooName"
109 )
110
111
112 result, err := edb.DB.Exec("INSERT INTO clusters (cluster_edge_id, cluster_name) VALUES ($1, $2)", clusterEdgeID, clusterName)
113 if err != nil {
114 t.Logf("Got result: %v", result)
115 t.Fatal(err)
116 } else if ra, err := result.RowsAffected(); err != nil {
117 t.Fatal(err)
118 } else if ra != 1 {
119 t.Fatalf("Got %d rows affected by insert", ra)
120 }
121
122 var now = time.Now().UTC().Round(time.Second)
123 var cluster = &gkeclusterApi.GKECluster{
124 TypeMeta: metav1.TypeMeta{
125 Kind: gkeclusterApi.Kind,
126 },
127 ObjectMeta: metav1.ObjectMeta{
128 Name: clusterEdgeID,
129 },
130 Status: gkeclusterApi.GKEClusterStatus{
131 Conditions: make([]metav1.Condition, 1),
132 },
133 }
134
135
136 cluster.Status.Conditions[0] = metav1.Condition{
137 Type: "Ready",
138 Status: metav1.ConditionTrue,
139 Message: "gkecluster reconciled successfully",
140 Reason: gkeclusterApi.GKEClusterReadyReason,
141 ObservedGeneration: 1,
142 LastTransitionTime: metav1.Time{Time: now},
143 }
144
145 edb.RecordInfraStatus(context.Background(), cluster, dbmetrics.DBMetrics{})
146 validateExpectedInfraStatus(t, cluster)
147
148
149 cluster.Status.Conditions[0] = metav1.Condition{
150 Type: "Ready",
151 Status: metav1.ConditionFalse,
152 Message: "oof",
153 Reason: "OofReason",
154 ObservedGeneration: 1,
155 LastTransitionTime: metav1.Time{Time: now},
156 }
157
158 edb.RecordInfraStatus(context.Background(), cluster, dbmetrics.DBMetrics{})
159 validateExpectedInfraStatus(t, cluster)
160 }
161
162
163 func testStatusForClusterAPI(t *testing.T) {
164 var (
165 clusterEdgeID = uuid.NewString()
166 clusterName = "clusterNameFoo"
167 )
168
169
170 result, err := edb.DB.Exec("INSERT INTO clusters (cluster_edge_id, cluster_name) VALUES ($1, $2)", clusterEdgeID, clusterName)
171 if err != nil {
172 t.Logf("Got result: %v", result)
173 t.Fatal(err)
174 } else if ra, err := result.RowsAffected(); err != nil {
175 t.Fatal(err)
176 } else if ra != 1 {
177 t.Fatalf("Got %d rows affected by insert", ra)
178 }
179
180 var now = time.Now().UTC().Round(time.Second)
181 var cluster = &clusterApi.Cluster{
182 TypeMeta: metav1.TypeMeta{
183 Kind: clusterApi.Kind,
184 },
185 ObjectMeta: metav1.ObjectMeta{
186 Name: clusterEdgeID,
187 },
188 Status: clusterApi.ClusterStatus{
189 Conditions: make([]metav1.Condition, 1),
190 },
191 }
192
193
194 cluster.Status.Conditions[0] = metav1.Condition{
195 Type: "Ready",
196 Status: metav1.ConditionTrue,
197 Message: "cluster reconciled successfully",
198 Reason: clusterApi.ClusterReadyReason,
199 ObservedGeneration: 1,
200 LastTransitionTime: metav1.Time{Time: now},
201 }
202
203 edb.RecordInfraStatus(context.Background(), cluster, dbmetrics.DBMetrics{})
204 validateExpectedInfraStatus(t, cluster)
205
206
207 cluster.Status.Conditions[0] = metav1.Condition{
208 Type: "Ready",
209 Status: metav1.ConditionFalse,
210 Message: "oof",
211 Reason: "OofReason",
212 ObservedGeneration: 1,
213 LastTransitionTime: metav1.Time{Time: now},
214 }
215
216 edb.RecordInfraStatus(context.Background(), cluster, dbmetrics.DBMetrics{})
217 validateExpectedInfraStatus(t, cluster)
218 }
219
220 const (
221 sqlSelectClusterInfraStatus = "SELECT infra_status, infra_status_details, infra_status_updated_at FROM clusters WHERE cluster_edge_id=$1"
222 sqlSelectBannerInfraStatus = "SELECT infra_status, infra_status_details, infra_status_updated_at FROM banners WHERE banner_edge_id=$1"
223 )
224
225 func validateExpectedInfraStatus(t *testing.T, obj conditions.Getter) {
226 var stmt string
227 switch obj.GetObjectKind().GroupVersionKind().Kind {
228 case clusterApi.Kind, gkeclusterApi.Kind:
229 stmt = sqlSelectClusterInfraStatus
230 case bannerApi.BannerKind:
231 stmt = sqlSelectBannerInfraStatus
232 default:
233 t.Fatalf("Unknown object %v", obj)
234 }
235
236 var edgeID = obj.GetName()
237 row := edb.DB.QueryRow(stmt, edgeID)
238 if err := row.Err(); err != nil {
239 t.Fatal(err)
240 }
241
242 var got struct {
243 Status string
244 Details string
245 UpdatedAt time.Time
246 }
247 err := row.Scan(&got.Status, &got.Details, &got.UpdatedAt)
248 if err != nil {
249 t.Fatal(err)
250 }
251
252
253 cond := obj.GetConditions()[0]
254 var enum InfraStatus
255 if cond.Status == metav1.ConditionTrue {
256 enum = InfraStatusReady
257 } else {
258 enum = InfraStatusError
259 }
260 var details = fmt.Sprintf("%s: %s", cond.Reason, cond.Message)
261 var when = cond.LastTransitionTime.Time
262
263 if got.Status != string(enum) {
264 t.Fatalf("Got infra_status %q and expected %q", got.Status, enum)
265 } else if got.Details != details {
266 t.Fatalf("Got infra_status_details %q and expected %q", got.Details, details)
267 } else if !got.UpdatedAt.Equal(when) {
268 t.Fatalf("Got infra_status_updated_at %q and expeceted %q", got.UpdatedAt, when)
269 }
270 t.Logf("successfully set InfraStatus=%q for %s", enum, obj.GetObjectKind().GroupVersionKind().Kind)
271 }
272
273 func testStoreClusterArtifacts(t *testing.T) {
274 storeID := "dc8e59c3-6338-4c28-a776-f54e93a19ff4"
275 artifacts, err := edb.GetClusterArtifactVersions(context.Background(), storeID)
276 if err != nil {
277 t.Fatal(err)
278 }
279 if len(artifacts) != 1 {
280 t.Fatalf("got %d artifact for store and expected 1", len(artifacts))
281 }
282 name := artifacts[0].Name
283 version := artifacts[0].Version
284 if name != "store" || version != "0.14.0-seed" {
285 t.Fatalf("got %s:%s artifact for store and expected store:0.14.0-seed", name, version)
286 }
287 }
288
View as plain text