Skip to content

Commit 8139416

Browse files
authored
Proxy API For Mkube (#145)
1 parent be5cd7f commit 8139416

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

restapi/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,8 @@ func getSecureFeaturePolicy() string {
228228
func getSecureExpectCTHeader() string {
229229
return env.Get(McsSecureExpectCTHeader, "")
230230
}
231+
232+
// getM3Host returns the hostname of mkube
233+
func getM3Host() string {
234+
return env.Get(McsM3Host, "http://m3:8787")
235+
}

restapi/configure_mcs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ func FileServerMiddleware(next http.Handler) http.Handler {
164164
switch {
165165
case strings.HasPrefix(r.URL.Path, "/ws"):
166166
serveWS(w, r)
167+
case strings.HasPrefix(r.URL.Path, "/api/v1/clusters"):
168+
client := &http.Client{}
169+
serverMkube(client, w, r)
167170
case strings.HasPrefix(r.URL.Path, "/api"):
168171
next.ServeHTTP(w, r)
169172
default:

restapi/consts.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ const (
4949
McsSecureReferrerPolicy = "MCS_SECURE_REFERRER_POLICY"
5050
McsSecureFeaturePolicy = "MCS_SECURE_FEATURE_POLICY"
5151
McsSecureExpectCTHeader = "MCS_SECURE_EXPECT_CT_HEADER"
52+
McsM3Host = "MCS_M3_HOSTNAME"
5253
)

restapi/mkube.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package restapi
18+
19+
import (
20+
"bufio"
21+
"errors"
22+
"fmt"
23+
"net/http"
24+
"strings"
25+
26+
apiErrors "github.com/go-openapi/errors"
27+
)
28+
29+
// serverMkube handles calls for mkube
30+
func serverMkube(client *http.Client, w http.ResponseWriter, req *http.Request) {
31+
// destination of the request, the mkube server
32+
targetURL := fmt.Sprintf("%s%s", getM3Host(), req.URL.String())
33+
34+
// set the HTTP method, url, and m3Req body
35+
m3Req, err := http.NewRequest(req.Method, targetURL, req.Body)
36+
if err != nil {
37+
apiErrors.ServeError(w, req, err)
38+
return
39+
}
40+
41+
// set the m3Req headers
42+
m3Req.Header = req.Header
43+
resp, err := client.Do(m3Req)
44+
if err != nil {
45+
if strings.Contains(err.Error(), "connection refused") {
46+
apiErrors.ServeError(w, req, errors.New("service M3 is not available"))
47+
return
48+
}
49+
apiErrors.ServeError(w, req, err)
50+
return
51+
}
52+
defer resp.Body.Close()
53+
w.Header().Add("Content-Type", resp.Header.Get("Content-Type"))
54+
// Write the m3 response to the response writer
55+
scanner := bufio.NewScanner(resp.Body)
56+
for scanner.Scan() {
57+
w.Write(scanner.Bytes())
58+
}
59+
if err := scanner.Err(); err != nil {
60+
apiErrors.ServeError(w, req, err)
61+
}
62+
63+
}

restapi/mkube_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package restapi
18+
19+
import (
20+
"bytes"
21+
"errors"
22+
"io/ioutil"
23+
"net/http"
24+
"net/http/httptest"
25+
"net/url"
26+
"testing"
27+
)
28+
29+
// RoundTripFunc .
30+
type RoundTripFunc func(req *http.Request) (*http.Response, error)
31+
32+
// RoundTrip .
33+
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
34+
return f(req)
35+
}
36+
37+
//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
38+
func NewTestClient(fn RoundTripFunc) *http.Client {
39+
return &http.Client{
40+
Transport: fn,
41+
}
42+
}
43+
44+
func Test_serverMkube(t *testing.T) {
45+
46+
OKclient := NewTestClient(func(req *http.Request) (*http.Response, error) {
47+
return &http.Response{
48+
StatusCode: 200,
49+
Body: ioutil.NopCloser(bytes.NewBufferString(`OK`)),
50+
Header: make(http.Header),
51+
}, nil
52+
})
53+
54+
badClient := NewTestClient(func(req *http.Request) (*http.Response, error) {
55+
return &http.Response{
56+
StatusCode: 500,
57+
Body: ioutil.NopCloser(bytes.NewBufferString(`NOTOK`)),
58+
Header: make(http.Header),
59+
}, errors.New("something wrong")
60+
})
61+
62+
refusedClient := NewTestClient(func(req *http.Request) (*http.Response, error) {
63+
return &http.Response{
64+
StatusCode: 500,
65+
Body: ioutil.NopCloser(bytes.NewBufferString(`NOTOK`)),
66+
Header: make(http.Header),
67+
}, errors.New("connection refused")
68+
})
69+
70+
testURL, _ := url.Parse("/api/v1/clusters")
71+
type args struct {
72+
client *http.Client
73+
recorder *httptest.ResponseRecorder
74+
req *http.Request
75+
}
76+
tests := []struct {
77+
name string
78+
args args
79+
wantCode int
80+
}{
81+
{
82+
name: "Successful request",
83+
args: args{
84+
client: OKclient,
85+
recorder: httptest.NewRecorder(),
86+
req: &http.Request{URL: testURL},
87+
},
88+
wantCode: 200,
89+
},
90+
{
91+
name: "Unsuccessful request",
92+
args: args{
93+
client: badClient,
94+
recorder: httptest.NewRecorder(),
95+
req: &http.Request{URL: testURL},
96+
},
97+
wantCode: 500,
98+
},
99+
{
100+
name: "refused request",
101+
args: args{
102+
client: refusedClient,
103+
recorder: httptest.NewRecorder(),
104+
req: &http.Request{URL: testURL},
105+
},
106+
wantCode: 500,
107+
},
108+
}
109+
for _, tt := range tests {
110+
t.Run(tt.name, func(t *testing.T) {
111+
serverMkube(tt.args.client, tt.args.recorder, tt.args.req)
112+
resp := tt.args.recorder.Result()
113+
if resp.StatusCode != tt.wantCode {
114+
t.Errorf("Invalid code returned")
115+
return
116+
}
117+
})
118+
}
119+
}

0 commit comments

Comments
 (0)