Skip to content
This repository was archived by the owner on Jul 1, 2023. It is now read-only.

Commit 945b614

Browse files
authored
Properly parse Gravity 5.0.36 status (#262)
* Add status tests for gravity 5.0.36. These two tests ensure that Robotest continues to work correctly when interacting with old versions of gravity (5.0.x). Contributes to #247. * Fix parsing of `gravity status` from gravity 5.0.x. For backwards compatibility the `system_status` json field needs to be able to accept `running` or `degraded` in addition to the integer values. Fixes #247.
1 parent 400f3eb commit 945b614

File tree

4 files changed

+209
-2
lines changed

4 files changed

+209
-2
lines changed

infra/gravity/gravity_test.go

+83
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ package gravity
1919
import (
2020
"bufio"
2121
"bytes"
22+
"encoding/json"
2223
"os"
2324
"testing"
2425

26+
"github.com/gravitational/robotest/lib/constants"
2527
"github.com/stretchr/testify/assert"
2628
)
2729

@@ -109,3 +111,84 @@ func Test1641StatusValidation(t *testing.T) {
109111
err = checkNotDegraded(status)
110112
assert.Error(t, err)
111113
}
114+
115+
// TestGravity5036ActiveStatusValidation ensures Robotest can correctly parse
116+
// an Active status from Gravity 5.0.36.
117+
//
118+
// This is important for testing --upgrade-via 5.0.x to 5.2.x to 5.5.x as well
119+
// as upgrade testing from stolon-app 1.10.x.
120+
//
121+
// See https://github.com/gravitational/robotest/issues/247 for more info.
122+
func TestGravity5036ActiveStatusValidation(t *testing.T) {
123+
f, err := os.Open("testdata/status-active-5.0.36.json")
124+
assert.NoError(t, err)
125+
defer f.Close()
126+
127+
var status GravityStatus
128+
err = parseStatus(&status)(bufio.NewReader(f))
129+
assert.NoError(t, err)
130+
131+
err = checkNotDegraded(status)
132+
assert.NoError(t, err)
133+
}
134+
135+
// TestGravity5036DegradedStatusValidation ensures Robotest can correctly parse
136+
// a Degraded status from Gravity 5.0.36.
137+
//
138+
// This is important for testing --upgrade-via 5.0.x to 5.2.x to 5.5.x as well
139+
// as upgrade testing from stolon-app 1.10.x.
140+
//
141+
// See https://github.com/gravitational/robotest/issues/247 for more info.
142+
func TestGravity5036DegradedStatusValidation(t *testing.T) {
143+
f, err := os.Open("testdata/status-degraded-5.0.36.json")
144+
assert.NoError(t, err)
145+
defer f.Close()
146+
147+
var status GravityStatus
148+
err = parseStatus(&status)(bufio.NewReader(f))
149+
assert.NoError(t, err)
150+
151+
err = checkNotDegraded(status)
152+
assert.Error(t, err)
153+
}
154+
155+
func TestSystemStatusStringDegradedUnmarshal(t *testing.T) {
156+
input := json.RawMessage(`"degraded"`)
157+
expected := constants.SystemStatus_Degraded
158+
var status int
159+
err := unmarshalSystemStatus(input, &status)
160+
assert.NoError(t, err)
161+
assert.Equal(t, expected, status)
162+
}
163+
164+
func TestSystemStatusStringRunningUnmarshal(t *testing.T) {
165+
input := json.RawMessage(`"running"`)
166+
expected := constants.SystemStatus_Running
167+
var status int
168+
err := unmarshalSystemStatus(input, &status)
169+
assert.NoError(t, err)
170+
assert.Equal(t, expected, status)
171+
}
172+
173+
func TestSystemStatusIntUnmarshal(t *testing.T) {
174+
input := json.RawMessage(`2`)
175+
expected := constants.SystemStatus_Degraded
176+
var status int
177+
err := unmarshalSystemStatus(input, &status)
178+
assert.NoError(t, err)
179+
assert.Equal(t, expected, status)
180+
}
181+
182+
func TestSystemStatusInvalidFloatUnmarshal(t *testing.T) {
183+
input := json.RawMessage(`3.14159`)
184+
var status int
185+
err := unmarshalSystemStatus(input, &status)
186+
assert.Error(t, err)
187+
}
188+
189+
func TestSystemStatusInvalidStringUnmarshal(t *testing.T) {
190+
input := json.RawMessage(`"working perfectly"`)
191+
var status int
192+
err := unmarshalSystemStatus(input, &status)
193+
assert.Error(t, err)
194+
}

infra/gravity/node_commands.go

+69-2
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,81 @@ type ClusterStatus struct {
155155
Cluster string `json:"domain"`
156156
// State is the cluster state
157157
State string `json:"state"`
158-
// SystemStatus is the cluster status, see https://github.com/gravitational/satellite/blob/7.1.0/agent/proto/agentpb/agent.proto#L50-L54
159-
SystemStatus int `json:"system_status"`
158+
// SystemStatus is the cluster status, custom unmarshalling logic needed to accommodate Gravity differences
159+
SystemStatus int `json:"-"`
160160
// Token is secure token which prevents rogue nodes from joining the cluster during installation
161161
Token Token `json:"token"`
162162
// Nodes describes the nodes in the cluster
163163
Nodes []NodeStatus `json:"nodes"`
164164
}
165165

166+
// unmarshalSystemStatus is a helper type to parse string or integer system statuses.
167+
//
168+
// For the original definitions and mappings see:
169+
// https://github.com/gravitational/satellite/blob/5.0.2/agent/proto/agentpb/agent.proto#L36-L40
170+
// https://github.com/gravitational/satellite/blob/5.0.2/agent/proto/agentpb/agent.pb.go#L48-L63
171+
func unmarshalSystemStatus(val json.RawMessage, status *int) error {
172+
err := json.Unmarshal(val, &status)
173+
if err == nil {
174+
return nil
175+
}
176+
// parsing as int failed, try parsing as a string and converting
177+
var str string
178+
if err2 := json.Unmarshal(val, &str); err2 != nil {
179+
return trace.BadParameter("unable to parse system_status %q as int or string: %v, %v", val, err, err2)
180+
}
181+
switch str {
182+
case "running":
183+
*status = constants.SystemStatus_Running
184+
case "degraded":
185+
*status = constants.SystemStatus_Degraded
186+
default:
187+
return trace.BadParameter("unknown system_status: %q", str)
188+
}
189+
return nil
190+
}
191+
192+
// UnmarshalJSON fufills the Unmarshaler interface, allowing ClusterStatus
193+
// to perform custom JSON unmarshalling for certain fields.
194+
//
195+
// This is needed for the system_status field which Gravity 5.2.x+ marshals as
196+
// an integer and Gravity 5.0.36- marshals as a string. When Gravity 5.0.x
197+
// support is no longer needed, this function and unmarshalSystemStatus can be
198+
// removed and replaced by the following field tag in the Cluster status type
199+
// definition:
200+
//
201+
// SystemStatus int `json:"system_status"`
202+
//
203+
// See https://github.com/gravitational/robotest/issues/247 for more info.
204+
func (cs *ClusterStatus) UnmarshalJSON(data []byte) error {
205+
// First, populate all fields that don't require special handling,
206+
type recursionBreaker ClusterStatus
207+
var temp recursionBreaker
208+
if err := json.Unmarshal(data, &temp); err != nil {
209+
return trace.Wrap(err)
210+
}
211+
*cs = ClusterStatus(temp)
212+
213+
// handle system_status, which may be a string or an int
214+
var jsonMap map[string]*json.RawMessage
215+
if err := json.Unmarshal(data, &jsonMap); err != nil {
216+
return trace.Wrap(err)
217+
}
218+
219+
val, ok := jsonMap["system_status"]
220+
if !ok {
221+
return trace.BadParameter("system_status field is required: %v", data)
222+
}
223+
224+
var status int
225+
if err := unmarshalSystemStatus(*val, &status); err != nil {
226+
return trace.Wrap(err)
227+
}
228+
229+
cs.SystemStatus = status
230+
return nil
231+
}
232+
166233
// Application defines the cluster application
167234
type Application struct {
168235
// Name is the name of the cluster application
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"cluster": {
3+
"application": {
4+
"repository": "gravitational.io",
5+
"name": "telekube",
6+
"version": "5.0.36"
7+
},
8+
"state": "active",
9+
"domain": "robotest-unit-test",
10+
"token": {
11+
"token": "ROBOTEST",
12+
"expires": "0001-01-01T00:00:00Z",
13+
"type": "expand",
14+
"account_id": "00000000-0000-0000-0000-000000000001",
15+
"site_domain": "robotest-unit-test",
16+
"operation_id": "",
17+
"user_email": "agent@robotest-unit-test"
18+
},
19+
"operation": {
20+
"type": "operation_install",
21+
"id": "6eb72cb8-56e6-4df4-a3f3-e971a4c64b80",
22+
"state": "completed",
23+
"created": "2020-08-18T00:20:18.854957796Z",
24+
"progress": {
25+
"message": "Operation has completed",
26+
"completion": 100,
27+
"created": "2020-08-18T00:20:27.428919029Z"
28+
}
29+
},
30+
"system_status": "running",
31+
"nodes": [
32+
{
33+
"hostname": "robotest-unit-test-node-0",
34+
"advertise_ip": "10.138.0.27",
35+
"role": "master",
36+
"status": "healthy"
37+
}
38+
]
39+
}
40+
}
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"cluster": {
3+
"system_status": "degraded",
4+
"nodes": [
5+
{
6+
"hostname": "",
7+
"advertise_ip": "10.138.0.112",
8+
"role": "master",
9+
"status": "degraded",
10+
"failed_probes": [
11+
"etcd-healthz failed"
12+
]
13+
}
14+
]
15+
}
16+
}

0 commit comments

Comments
 (0)