Skip to content

Commit 4a2a84d

Browse files
authored
Merge pull request #95 from imperva/data-centers-configuration
Data centers configuration
2 parents 5799558 + 45ca64d commit 4a2a84d

28 files changed

+1865
-73
lines changed

GNUmakefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ HOSTNAME=registry.terraform.io
55
NAMESPACE=terraform-providers
66
PKG_NAME=incapsula
77
BINARY=terraform-provider-${PKG_NAME}
8-
VERSION=2.9.0
8+
# Whenever bumping provider version, please update the version in incapsula/client.go (line 27) as well.
9+
VERSION=3.0.0
910
# OS_ARCH=darwin_amd64
11+
# OS_ARCH=linux_amd64
1012

1113
default: install
1214

incapsula/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Client struct {
2424
// NewClient creates a new client with the provided configuration
2525
func NewClient(config *Config) *Client {
2626
client := &http.Client{}
27-
return &Client{config: config, httpClient: client, providerVersion: "2.9.0"}
27+
return &Client{config: config, httpClient: client, providerVersion: "3.0.0"}
2828
}
2929

3030
// Verify checks the API credentials

incapsula/client_cache_rule.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io/ioutil"
77
"log"
88
"net/http"
9+
"strings"
910
)
1011

1112
// CacheRule is a struct that encompasses all the properties of a CacheRule
@@ -60,7 +61,7 @@ func (c *Client) AddCacheRule(siteID string, rule *CacheRule) (*CacheRuleWithID,
6061
// Parse the JSON
6162
var cacheRuleWithID CacheRuleWithID
6263
err = json.Unmarshal([]byte(responseBody), &cacheRuleWithID)
63-
if err != nil {
64+
if err != nil || !strings.Contains(string(responseBody), "\"rule_id\":") {
6465
return nil, fmt.Errorf("Error parsing Cache Rule JSON response for Site ID %s: %s\nresponse: %s", siteID, err, string(responseBody))
6566
}
6667

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package incapsula
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"net/http"
9+
)
10+
11+
type OriginServerStruct struct {
12+
Address string `json:"address"`
13+
IsEnabled bool `json:"isEnabled"`
14+
ServerMode string `json:"serverMode"`
15+
Weight *int `json:"weight"`
16+
}
17+
18+
type DataCenterStruct struct {
19+
Name string `json:"name"`
20+
ID *int `json:"id"`
21+
IpMode string `json:"ipMode"`
22+
WebServersPerServer *int `json:"webServersPerServer"`
23+
DcLbAlgorithm string `json:"lbAlgorithm"`
24+
Weight *int `json:"weight"`
25+
IsEnabled bool `json:"isEnabled"`
26+
IsActive bool `json:"isActive"`
27+
IsContent bool `json:"isContent"`
28+
IsRestOfTheWorld bool `json:"isRestOfTheWorld"`
29+
GeoLocations []string `json:"geoLocations"`
30+
OriginPoP string `json:"originPop"`
31+
OriginServers []OriginServerStruct `json:"servers"`
32+
}
33+
34+
type DataCentersStruct struct {
35+
SiteLbAlgorithm string `json:"lbAlgorithm"`
36+
FailOverRequiredMonitors string `json:"failOverRequiredMonitors"`
37+
DataCenterMode string `json:"dataCenterMode"`
38+
MinAvailableServersForDataCenterUp int `json:"minAvailableServersForDataCenterUp"`
39+
KickStartURL string `json:"kickStartURL"`
40+
KickStartUser string `json:"kickStartUser"`
41+
KickStartPass string `json:"kickStartPass"`
42+
IsPersistent bool `json:"isPersistent"`
43+
DataCenters []DataCenterStruct `json:"dataCenters"`
44+
}
45+
46+
type ApiErrorSource struct {
47+
Pointer string `json:"pointer"`
48+
Parameter string `json:"parameter"`
49+
}
50+
51+
type ApiError struct {
52+
ID string `json:"id"`
53+
Status string `json:"status"`
54+
Code string `json:"code"`
55+
Message string `json:"message"`
56+
Source ApiErrorSource `json:"source"`
57+
}
58+
59+
// Same DTO for: GET response, PUT request, and PUT response
60+
type DataCentersConfigurationDTO struct {
61+
Errors []ApiError `json:"errors"`
62+
Data []DataCentersStruct `json:"data"`
63+
}
64+
65+
// AddDataCenter adds an incap rule to be managed by Incapsula
66+
func (c *Client) PutDataCentersConfiguration(siteID string, requestDTO DataCentersConfigurationDTO) (*DataCentersConfigurationDTO, error) {
67+
log.Printf("[INFO] Updating Incapsula data centers configuration for siteID: %s\n", siteID)
68+
69+
baseURLv3 := c.config.BaseURL[:len(c.config.BaseURL)-3] + "/v3"
70+
dcsJSON, err := json.Marshal(requestDTO)
71+
reqURL := fmt.Sprintf("%s/sites/%s/data-centers-configuration", baseURLv3, siteID)
72+
resp, err := c.DoJsonRequestWithHeaders(http.MethodPut, reqURL, dcsJSON)
73+
if err != nil {
74+
return nil, fmt.Errorf("Error executing update Data Centers configuration request for siteID %s: %s", siteID, err)
75+
}
76+
77+
// Read the body
78+
defer resp.Body.Close()
79+
responseBody, err := ioutil.ReadAll(resp.Body)
80+
81+
// Dump JSON
82+
log.Printf("[DEBUG] Incapsula Update Data Centers configuration JSON response: %s\n", string(responseBody))
83+
84+
// Parse the JSON
85+
var responseDTO DataCentersConfigurationDTO
86+
err = json.Unmarshal([]byte(responseBody), &responseDTO)
87+
if err != nil {
88+
return nil, fmt.Errorf("Error parsing update Data Centers configuration JSON response for siteID %s: %s\nresponse: %s", siteID, err, string(responseBody))
89+
}
90+
91+
return &responseDTO, nil
92+
}
93+
94+
// ListDataCenters gets the Incapsula list of data centers
95+
func (c *Client) GetDataCentersConfiguration(siteID string) (*DataCentersConfigurationDTO, error) {
96+
log.Printf("[INFO] Getting Data Centers configuration (site_id: %s)\n", siteID)
97+
98+
// Get request to Incapsula
99+
baseURLv3 := c.config.BaseURL[:len(c.config.BaseURL)-3] + "/v3"
100+
reqURL := fmt.Sprintf("%s/sites/%s/data-centers-configuration", baseURLv3, siteID)
101+
resp, err := c.DoJsonRequestWithHeaders(http.MethodGet, reqURL, nil)
102+
if err != nil {
103+
return nil, fmt.Errorf("Error executing get Data Centers configuration request for siteID %s: %s", siteID, err)
104+
}
105+
106+
// Read the body
107+
defer resp.Body.Close()
108+
responseBody, err := ioutil.ReadAll(resp.Body)
109+
110+
// Dump JSON
111+
log.Printf("[DEBUG] Incapsula data centers JSON response: %s\n", string(responseBody))
112+
113+
// Parse the JSON
114+
var responseDTO DataCentersConfigurationDTO
115+
err = json.Unmarshal([]byte(responseBody), &responseDTO)
116+
if err != nil {
117+
return nil, fmt.Errorf("Error parsing data centers list JSON response for siteID: %s %s\nresponse: %s", siteID, err, string(responseBody))
118+
}
119+
120+
return &responseDTO, nil
121+
}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package incapsula
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"net/http/httptest"
7+
"strings"
8+
"testing"
9+
"time"
10+
)
11+
12+
////////////////////////////////////////////////////////////////
13+
// AddDataCenter Tests
14+
////////////////////////////////////////////////////////////////
15+
16+
func TestClientPutDataCentersConfigurationBadConnection(t *testing.T) {
17+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: "badness.incapsula.com"}
18+
client := &Client{config: config, httpClient: &http.Client{Timeout: time.Millisecond * 1}}
19+
siteID := "42"
20+
requestDTO := DataCentersConfigurationDTO{}
21+
responseDTO, err := client.PutDataCentersConfiguration(siteID, requestDTO)
22+
if err == nil {
23+
t.Errorf("Should have received an error")
24+
}
25+
if !strings.HasPrefix(err.Error(), fmt.Sprintf("Error executing update Data Centers configuration request for siteID %s", siteID)) {
26+
t.Errorf("Should have received an client error, got: %s", err)
27+
}
28+
if responseDTO != nil {
29+
t.Errorf("Should have received a nil responseDTO instance")
30+
}
31+
}
32+
33+
func TestClientPutDataCentersConfigurationBadJSON(t *testing.T) {
34+
siteID := "42"
35+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
36+
if req.URL.String() != fmt.Sprintf("/api/prov/v3/sites/%s/data-centers-configuration", siteID) {
37+
t.Errorf("Should have have hit /api/prov/v3/sites/%s/data-centers-configurations endpoint. "+
38+
"Got: %s", siteID, req.URL.String())
39+
}
40+
rw.Write([]byte(`{`))
41+
}))
42+
defer server.Close()
43+
44+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: server.URL + "/api/prov/v1"}
45+
client := &Client{config: config, httpClient: &http.Client{}}
46+
requestDTO := DataCentersConfigurationDTO{}
47+
responseDTO, err := client.PutDataCentersConfiguration(siteID, requestDTO)
48+
if err == nil {
49+
t.Errorf("Should have received an error")
50+
}
51+
if !strings.HasPrefix(err.Error(), fmt.Sprintf("Error parsing update Data Centers configuration JSON response for siteID %s", siteID)) {
52+
t.Errorf("Should have received a JSON parse error, got: %s", err)
53+
}
54+
if responseDTO != nil {
55+
t.Errorf("Should have received a nil responseDTO instance")
56+
}
57+
}
58+
59+
func TestClientPutDataCenterInvalidDcConfiguration(t *testing.T) {
60+
siteID := "42"
61+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
62+
if req.URL.String() != fmt.Sprintf("/api/prov/v3/sites/%s/data-centers-configuration", siteID) {
63+
t.Errorf("Should have have hit /api/prov/v3/sites/%s/data-centers-configurations endpoint. "+
64+
"Got: %s", siteID, req.URL.String())
65+
}
66+
rw.Write([]byte(`{"errors":[{"status": "406"}]}`))
67+
}))
68+
defer server.Close()
69+
70+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: server.URL + "/api/prov/v1"}
71+
client := &Client{config: config, httpClient: &http.Client{}}
72+
requestDTO := DataCentersConfigurationDTO{}
73+
responseDTO, err := client.PutDataCentersConfiguration(siteID, requestDTO)
74+
if err != nil {
75+
t.Errorf("Should not receive an error. Got: %s", err.Error())
76+
}
77+
if responseDTO == nil || responseDTO.Errors == nil || len(responseDTO.Errors) < 1 {
78+
t.Errorf("Should have received a response DTO instance with at least one error item")
79+
return
80+
}
81+
if responseDTO.Errors[0].Status != "406" {
82+
t.Errorf("Should have received a bad DC configuration error, got: %s", err)
83+
}
84+
}
85+
86+
func TestClientPutDataCenterValidDcConfiguration(t *testing.T) {
87+
siteID := "42"
88+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
89+
if req.URL.String() != fmt.Sprintf("/api/prov/v3/sites/%s/data-centers-configuration", siteID) {
90+
t.Errorf("Should have have hit /api/prov/v3/sites/%s/data-centers-configurations endpoint. "+
91+
"Got: %s", siteID, req.URL.String())
92+
}
93+
rw.Write([]byte(`{"data":[{"dataCenterMode":"SINGLE_DC","dataCenters":[{"name":"New DC","servers":[{"address":"1.2.3.4"}]}]}]}`))
94+
}))
95+
defer server.Close()
96+
97+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: server.URL + "/api/prov/v1"}
98+
client := &Client{config: config, httpClient: &http.Client{}}
99+
requestDTO := DataCentersConfigurationDTO{}
100+
responseDTO, err := client.PutDataCentersConfiguration(siteID, requestDTO)
101+
if err != nil {
102+
t.Errorf("Should not have received an error. Got: %s", err.Error())
103+
}
104+
if responseDTO == nil {
105+
t.Errorf("Should not have received a nil response DTO instance")
106+
return
107+
}
108+
if responseDTO.Data == nil || len(responseDTO.Data) < 1 || responseDTO.Data[0].DataCenters == nil ||
109+
len(responseDTO.Data[0].DataCenters) < 1 || responseDTO.Data[0].DataCenters[0].OriginServers == nil ||
110+
len(responseDTO.Data[0].DataCenters[0].OriginServers) < 1 {
111+
t.Errorf("Response must contain one Data Center, which contains one Origin Server. Items: %d", len(responseDTO.Data))
112+
t.Errorf("Response must contain one Data Center, which contains one Origin Server. Data Centers: %d",
113+
len(responseDTO.Data[0].DataCenters))
114+
t.Errorf("Response must contain one Data Center, which contains one Origin Server. Origin Servers: %d",
115+
len(responseDTO.Data[0].DataCenters[0].OriginServers))
116+
}
117+
}
118+
119+
////////////////////////////////////////////////////////////////
120+
// ListDataCenters Tests
121+
////////////////////////////////////////////////////////////////
122+
123+
func TestClientGetDataCentersConfigurationBadConnection(t *testing.T) {
124+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: "badness.incapsula.com"}
125+
client := &Client{config: config, httpClient: &http.Client{Timeout: time.Millisecond * 1}}
126+
siteID := "42"
127+
responseDTO, err := client.GetDataCentersConfiguration(siteID)
128+
if err == nil {
129+
t.Errorf("Should have received an error")
130+
}
131+
if !strings.HasPrefix(err.Error(), fmt.Sprintf(
132+
"Error executing get Data Centers configuration request for siteID %s", siteID)) {
133+
t.Errorf("Should have received a client error, got: %s", err)
134+
}
135+
if responseDTO != nil {
136+
t.Errorf("Should have received a nil responseDTO instance")
137+
}
138+
}
139+
140+
func TestClientGetDataCentersConfigurationBadJSON(t *testing.T) {
141+
siteID := "42"
142+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
143+
if req.URL.String() != fmt.Sprintf("/api/prov/v3/sites/%s/data-centers-configuration", siteID) {
144+
t.Errorf("Should have have hit /api/prov/v3/sites/%s/data-centers-configurations endpoint. "+
145+
"Got: %s", siteID, req.URL.String())
146+
}
147+
rw.Write([]byte(`{`))
148+
}))
149+
defer server.Close()
150+
151+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: server.URL + "/api/prov/v1"}
152+
client := &Client{config: config, httpClient: &http.Client{}}
153+
responseDTO, err := client.GetDataCentersConfiguration(siteID)
154+
if err == nil {
155+
t.Errorf("Should have received an error")
156+
}
157+
if !strings.HasPrefix(err.Error(), fmt.Sprintf("Error parsing data centers list JSON response for siteID: %s", siteID)) {
158+
t.Errorf("Should have received a JSON parse error, got: %s", err)
159+
}
160+
if responseDTO != nil {
161+
t.Errorf("Should have received a nil responseDTO instance")
162+
}
163+
}
164+
165+
func TestClientGetDataCentersConfigurationInvalidRequest(t *testing.T) {
166+
siteID := "42"
167+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
168+
if req.URL.String() != fmt.Sprintf("/api/prov/v3/sites/%s/data-centers-configuration", siteID) {
169+
t.Errorf("Should have have hit /api/prov/v3/sites/%s/data-centers-configurations endpoint. "+
170+
"Got: %s", siteID, req.URL.String())
171+
}
172+
rw.Write([]byte(`{"errors":[{"status": "404"}]}`))
173+
}))
174+
defer server.Close()
175+
176+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: server.URL + "/api/prov/v1"}
177+
client := &Client{config: config, httpClient: &http.Client{}}
178+
responseDTO, err := client.GetDataCentersConfiguration(siteID)
179+
if err != nil {
180+
t.Errorf("Should not receive an error. Got: %s", err.Error())
181+
}
182+
if responseDTO == nil || responseDTO.Errors == nil || len(responseDTO.Errors) < 1 {
183+
t.Errorf("Should have received a response DTO instance with at least one error item")
184+
return
185+
}
186+
if responseDTO.Errors[0].Status != "404" {
187+
t.Errorf("Should have received a bad DC configuration error, got: %s", err)
188+
}
189+
}
190+
191+
func TestClientGetDataCentersConfigurationValidRequest(t *testing.T) {
192+
siteID := "42"
193+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
194+
if req.URL.String() != fmt.Sprintf("/api/prov/v3/sites/%s/data-centers-configuration", siteID) {
195+
t.Errorf("Should have have hit /api/prov/v3/sites/%s/data-centers-configurations endpoint. "+
196+
"Got: %s", siteID, req.URL.String())
197+
}
198+
rw.Write([]byte(`{"data":[{"dataCenterMode":"SINGLE_DC","dataCenters":[{"name":"New DC","servers":[{"address":"1.2.3.4"}]}]}]}`))
199+
}))
200+
defer server.Close()
201+
202+
config := &Config{APIID: "foo", APIKey: "bar", BaseURL: server.URL + "/api/prov/v1"}
203+
client := &Client{config: config, httpClient: &http.Client{}}
204+
responseDTO, err := client.GetDataCentersConfiguration(siteID)
205+
if err != nil {
206+
t.Errorf("Should not have received an error")
207+
}
208+
if responseDTO == nil {
209+
t.Errorf("Should not have received a nil responseDTO instance")
210+
}
211+
212+
if responseDTO.Data == nil || len(responseDTO.Data) < 1 || responseDTO.Data[0].DataCenters == nil ||
213+
len(responseDTO.Data[0].DataCenters) < 1 || responseDTO.Data[0].DataCenters[0].OriginServers == nil ||
214+
len(responseDTO.Data[0].DataCenters[0].OriginServers) < 1 {
215+
t.Errorf("Response must contain one Data Center, which contains one Origin Server. Items: %d", len(responseDTO.Data))
216+
t.Errorf("Response must contain one Data Center, which contains one Origin Server. Data Centers: %d",
217+
len(responseDTO.Data[0].DataCenters))
218+
t.Errorf("Response must contain one Data Center, which contains one Origin Server. Origin Servers: %d",
219+
len(responseDTO.Data[0].DataCenters[0].OriginServers))
220+
}
221+
}

0 commit comments

Comments
 (0)