Skip to content

Commit f076ebf

Browse files
authored
feat: add outage simulation (#350)
1 parent d8b9275 commit f076ebf

File tree

4 files changed

+356
-1
lines changed

4 files changed

+356
-1
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright 2023 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package mongodbatlas
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"net/http"
21+
)
22+
23+
const (
24+
clusterOutageSimulationPath = "api/atlas/v1.0/groups/%s/clusters/%s/outageSimulation"
25+
)
26+
27+
// ClusterOutageSimulationService is an interface for interfacing with the cluster outage endpoints of the MongoDB Atlas API.
28+
//
29+
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cluster-Outage-Simulation
30+
type ClusterOutageSimulationService interface {
31+
EndOutageSimulation(context.Context, string, string) (*ClusterOutageSimulation, *Response, error)
32+
GetOutageSimulation(context.Context, string, string) (*ClusterOutageSimulation, *Response, error)
33+
StartOutageSimulation(context.Context, string, string, *ClusterOutageSimulationRequest) (*ClusterOutageSimulation, *Response, error)
34+
}
35+
36+
// ClusterOutageSimulationServiceOp handles communication with the ClusterOutageSimulationService related methods of the
37+
// MongoDB Atlas API.
38+
type ClusterOutageSimulationServiceOp service
39+
40+
var _ ClusterOutageSimulationService = &ClusterOutageSimulationServiceOp{}
41+
42+
type ClusterOutageSimulation struct {
43+
// Human-readable label that identifies the cluster that undergoes outage simulation.
44+
ClusterName *string `json:"clusterName,omitempty"`
45+
// Unique 24-hexadecimal character string that identifies the project that contains the cluster to undergo outage simulation.
46+
GroupID *string `json:"groupId,omitempty"`
47+
// Unique 24-hexadecimal character string that identifies the outage simulation.
48+
ID *string `json:"id,omitempty"`
49+
// List of settings that specify the type of cluster outage simulation.
50+
OutageFilters []ClusterOutageSimulationOutageFilter `json:"outageFilters,omitempty"`
51+
// Date and time when MongoDB Cloud started the regional outage simulation.
52+
StartRequestDate *string `json:"startRequestDate,omitempty"`
53+
// Phase of the outage simulation. | State | Indication | |-------------|------------| | `START_REQUESTED` | User has requested cluster outage simulation.| | `STARTING` | MongoDB Cloud is starting cluster outage simulation.| | `SIMULATING` | MongoDB Cloud is simulating cluster outage.| | `RECOVERY_REQUESTED` | User has requested recovery from the simulated outage.| | `RECOVERING` | MongoDB Cloud is recovering the cluster from the simulated outage.| | `COMPLETE` | MongoDB Cloud has completed the cluster outage simulation.|
54+
State *string `json:"state,omitempty"`
55+
}
56+
57+
type ClusterOutageSimulationRequest struct {
58+
OutageFilters []ClusterOutageSimulationOutageFilter `json:"outageFilters,omitempty"`
59+
}
60+
61+
type ClusterOutageSimulationOutageFilter struct {
62+
// The cloud provider of the region that undergoes the outage simulation.
63+
CloudProvider *string `json:"cloudProvider,omitempty"`
64+
// The name of the region to undergo an outage simulation.
65+
RegionName *string `json:"regionName,omitempty"`
66+
// The type of cluster outage to simulate. | Type | Description | |------------|-------------| | `REGION` | Simulates a cluster outage for a region.|
67+
Type *string `json:"type,omitempty"`
68+
}
69+
70+
// EndOutageSimulation ends a cluster outage simulation.
71+
//
72+
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cluster-Outage-Simulation/operation/endOutageSimulation
73+
func (s ClusterOutageSimulationServiceOp) EndOutageSimulation(ctx context.Context, groupID, clusterName string) (*ClusterOutageSimulation, *Response, error) {
74+
if groupID == "" {
75+
return nil, nil, NewArgError("groupId", "must be set")
76+
}
77+
if clusterName == "" {
78+
return nil, nil, NewArgError("clusterName", "must be set")
79+
}
80+
81+
path := fmt.Sprintf(clusterOutageSimulationPath, groupID, clusterName)
82+
83+
req, err := s.Client.NewRequest(ctx, http.MethodDelete, path, nil)
84+
if err != nil {
85+
return nil, nil, err
86+
}
87+
88+
root := new(ClusterOutageSimulation)
89+
resp, err := s.Client.Do(ctx, req, root)
90+
if err != nil {
91+
return nil, resp, err
92+
}
93+
94+
return root, resp, err
95+
}
96+
97+
// GetOutageSimulation returns one outage simulation for one cluster.
98+
//
99+
// See more: https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cluster-Outage-Simulation/operation/getOutageSimulation
100+
func (s ClusterOutageSimulationServiceOp) GetOutageSimulation(ctx context.Context, groupID, clusterName string) (*ClusterOutageSimulation, *Response, error) {
101+
if groupID == "" {
102+
return nil, nil, NewArgError("groupId", "must be set")
103+
}
104+
if clusterName == "" {
105+
return nil, nil, NewArgError("clusterName", "must be set")
106+
}
107+
108+
path := fmt.Sprintf(clusterOutageSimulationPath, groupID, clusterName)
109+
110+
req, err := s.Client.NewRequest(ctx, http.MethodGet, path, nil)
111+
if err != nil {
112+
return nil, nil, err
113+
}
114+
115+
root := new(ClusterOutageSimulation)
116+
resp, err := s.Client.Do(ctx, req, root)
117+
if err != nil {
118+
return nil, resp, err
119+
}
120+
121+
return root, resp, err
122+
}
123+
124+
// StartOutageSimulation starts a cluster outage simulation.
125+
//
126+
// See more:https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Cluster-Outage-Simulation/operation/startOutageSimulation
127+
func (s ClusterOutageSimulationServiceOp) StartOutageSimulation(ctx context.Context, groupID, clusterName string, request *ClusterOutageSimulationRequest) (*ClusterOutageSimulation, *Response, error) {
128+
if groupID == "" {
129+
return nil, nil, NewArgError("groupId", "must be set")
130+
}
131+
if clusterName == "" {
132+
return nil, nil, NewArgError("clusterName", "must be set")
133+
}
134+
if request == nil {
135+
return nil, nil, NewArgError("request", "cannot be nil")
136+
}
137+
138+
path := fmt.Sprintf(clusterOutageSimulationPath, groupID, clusterName)
139+
fmt.Println(path)
140+
req, err := s.Client.NewRequest(ctx, http.MethodPost, path, request)
141+
if err != nil {
142+
return nil, nil, err
143+
}
144+
145+
root := new(ClusterOutageSimulation)
146+
resp, err := s.Client.Do(ctx, req, root)
147+
if err != nil {
148+
return nil, resp, err
149+
}
150+
151+
return root, resp, err
152+
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright 2023 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package mongodbatlas
16+
17+
import (
18+
"encoding/json"
19+
"fmt"
20+
"net/http"
21+
"testing"
22+
23+
"github.com/go-test/deep"
24+
"github.com/openlyinc/pointy"
25+
)
26+
27+
func TestClusterOutageSimulationServiceOp_GetOutageSimulation(t *testing.T) {
28+
client, mux, teardown := setup()
29+
defer teardown()
30+
31+
path := fmt.Sprintf("/api/atlas/v1.0/groups/%s/clusters/%s/outageSimulation", groupID, clusterName)
32+
33+
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
34+
testMethod(t, r, http.MethodGet)
35+
fmt.Fprintf(w, `{
36+
"clusterName": "%s",
37+
"groupId": "%s",
38+
"id": "63599c987d469e386156afcb",
39+
"outageFilters": [
40+
{
41+
"cloudProvider": "AWS",
42+
"regionName": "string",
43+
"type": "REGION"
44+
}
45+
],
46+
"startRequestDate": "2022-01-01T00:00:00Z",
47+
"state": "START_REQUESTED"
48+
}`, clusterName, groupID)
49+
})
50+
51+
simulation, _, err := client.ClusterOutageSimulation.GetOutageSimulation(ctx, groupID, clusterName)
52+
if err != nil {
53+
t.Fatalf("ClusterOutageSimulation.GetOutageSimulation returned error: %v", err)
54+
}
55+
56+
expected := &ClusterOutageSimulation{
57+
ClusterName: pointy.String(clusterName),
58+
GroupID: pointy.String(groupID),
59+
ID: pointy.String("63599c987d469e386156afcb"),
60+
OutageFilters: []ClusterOutageSimulationOutageFilter{
61+
{
62+
CloudProvider: pointy.String("AWS"),
63+
RegionName: pointy.String("string"),
64+
Type: pointy.String("REGION"),
65+
},
66+
},
67+
StartRequestDate: pointy.String("2022-01-01T00:00:00Z"),
68+
State: pointy.String("START_REQUESTED"),
69+
}
70+
71+
if diff := deep.Equal(simulation, expected); diff != nil {
72+
t.Error(diff)
73+
}
74+
}
75+
76+
func TestClusterOutageSimulationServiceOp_EndOutageSimulation(t *testing.T) {
77+
client, mux, teardown := setup()
78+
defer teardown()
79+
80+
path := fmt.Sprintf("/api/atlas/v1.0/groups/%s/clusters/%s/outageSimulation", groupID, clusterName)
81+
82+
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
83+
testMethod(t, r, http.MethodDelete)
84+
fmt.Fprintf(w, `{
85+
"clusterName": "%s",
86+
"groupId": "%s",
87+
"id": "63599c987d469e386156afcb",
88+
"outageFilters": [
89+
{
90+
"cloudProvider": "AWS",
91+
"regionName": "string",
92+
"type": "REGION"
93+
}
94+
],
95+
"startRequestDate": "2022-01-01T00:00:00Z",
96+
"state": "START_REQUESTED"
97+
}`, clusterName, groupID)
98+
})
99+
100+
simulation, _, err := client.ClusterOutageSimulation.EndOutageSimulation(ctx, groupID, clusterName)
101+
if err != nil {
102+
t.Fatalf("ClusterOutageSimulation.EndOutageSimulation returned error: %v", err)
103+
}
104+
105+
expected := &ClusterOutageSimulation{
106+
ClusterName: pointy.String(clusterName),
107+
GroupID: pointy.String(groupID),
108+
ID: pointy.String("63599c987d469e386156afcb"),
109+
OutageFilters: []ClusterOutageSimulationOutageFilter{
110+
{
111+
CloudProvider: pointy.String("AWS"),
112+
RegionName: pointy.String("string"),
113+
Type: pointy.String("REGION"),
114+
},
115+
},
116+
StartRequestDate: pointy.String("2022-01-01T00:00:00Z"),
117+
State: pointy.String("START_REQUESTED"),
118+
}
119+
120+
if diff := deep.Equal(simulation, expected); diff != nil {
121+
t.Error(diff)
122+
}
123+
}
124+
125+
func TestClusterOutageSimulationServiceOp_StartOutageSimulation(t *testing.T) {
126+
client, mux, teardown := setup()
127+
defer teardown()
128+
129+
path := fmt.Sprintf("/api/atlas/v1.0/groups/%s/clusters/%s/outageSimulation", groupID, clusterName)
130+
131+
createRequest := &ClusterOutageSimulationRequest{
132+
OutageFilters: []ClusterOutageSimulationOutageFilter{
133+
{
134+
CloudProvider: pointy.String("AWS"),
135+
RegionName: pointy.String("string"),
136+
Type: pointy.String("REGION"),
137+
},
138+
},
139+
}
140+
141+
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
142+
testMethod(t, r, http.MethodPost)
143+
expected := map[string]interface{}{
144+
"outageFilters": []interface{}{
145+
map[string]interface{}{
146+
"cloudProvider": "AWS",
147+
"regionName": "string",
148+
"type": "REGION",
149+
},
150+
},
151+
}
152+
153+
var v map[string]interface{}
154+
err := json.NewDecoder(r.Body).Decode(&v)
155+
if err != nil {
156+
t.Fatalf("decode json: %v", err.Error())
157+
}
158+
159+
if diff := deep.Equal(v, expected); diff != nil {
160+
t.Error(diff)
161+
}
162+
fmt.Fprintf(w, `{
163+
"clusterName": "%s",
164+
"groupId": "%s",
165+
"id": "63599c987d469e386156afcb",
166+
"outageFilters": [
167+
{
168+
"cloudProvider": "AWS",
169+
"regionName": "string",
170+
"type": "REGION"
171+
}
172+
],
173+
"startRequestDate": "2022-01-01T00:00:00Z",
174+
"state": "START_REQUESTED"
175+
}`, clusterName, groupID)
176+
})
177+
178+
simulation, _, err := client.ClusterOutageSimulation.StartOutageSimulation(ctx, groupID, clusterName, createRequest)
179+
if err != nil {
180+
t.Fatalf("ClusterOutageSimulation.StartOutageSimulation returned error: %v", err)
181+
}
182+
183+
expected := &ClusterOutageSimulation{
184+
ClusterName: pointy.String(clusterName),
185+
GroupID: pointy.String(groupID),
186+
ID: pointy.String("63599c987d469e386156afcb"),
187+
OutageFilters: []ClusterOutageSimulationOutageFilter{
188+
{
189+
CloudProvider: pointy.String("AWS"),
190+
RegionName: pointy.String("string"),
191+
Type: pointy.String("REGION"),
192+
},
193+
},
194+
StartRequestDate: pointy.String("2022-01-01T00:00:00Z"),
195+
State: pointy.String("START_REQUESTED"),
196+
}
197+
198+
if diff := deep.Equal(simulation, expected); diff != nil {
199+
t.Error(diff)
200+
}
201+
}

mongodbatlas/mongodbatlas.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ type Client struct {
152152
CloudProviderSnapshotExportBuckets CloudProviderSnapshotExportBucketsService
153153
CloudProviderSnapshotExportJobs CloudProviderSnapshotExportJobsService
154154
FederatedSettings FederatedSettingsService
155+
ClusterOutageSimulation ClusterOutageSimulationService
155156

156157
onRequestCompleted RequestCompletionCallback
157158
onResponseProcessed ResponseProcessedCallback
@@ -300,6 +301,7 @@ func NewClient(httpClient *http.Client) *Client {
300301
c.CloudProviderSnapshotExportBuckets = &CloudProviderSnapshotExportBucketsServiceOp{Client: c}
301302
c.CloudProviderSnapshotExportJobs = &CloudProviderSnapshotExportJobsServiceOp{Client: c}
302303
c.FederatedSettings = &FederatedSettingsServiceOp{Client: c}
304+
c.ClusterOutageSimulation = &ClusterOutageSimulationServiceOp{Client: c}
303305
return c
304306
}
305307

mongodbatlas/mongodbatlas_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func setup() (client *Client, mux *http.ServeMux, teardown func()) {
7676
func testMethod(t *testing.T, r *http.Request, expected string) {
7777
t.Helper()
7878
if expected != r.Method {
79-
t.Errorf("Request method = %v, expected %v", r.Method, expected)
79+
t.Fatalf("Request method = %q, expected %q", r.Method, expected)
8080
}
8181
}
8282

0 commit comments

Comments
 (0)