Skip to content

Commit bb2e3f0

Browse files
authored
Merge pull request #71 from mitre/resolve-gcp-loc
Resolve location on GCP
2 parents e01dcbb + 5c285b8 commit bb2e3f0

File tree

140 files changed

+46596
-133
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+46596
-133
lines changed

Gopkg.lock

+46-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

awsutil/aws.go

+173
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
package awsutil
1616

1717
import (
18+
"encoding/json"
1819
"io"
1920
"io/ioutil"
2021
"net"
2122
"net/http"
2223
"net/url"
24+
"path/filepath"
2325
"strings"
2426
"syscall"
2527
"time"
@@ -142,6 +144,98 @@ func ReadNgcFile(path string) ([]byte, error) {
142144
return bytes, err
143145
}
144146

147+
func ResolveRegion() (string, error) {
148+
// Attempt to resolve the location on aws or gs.
149+
loc, err := resolveAwsRegion()
150+
if err != nil {
151+
// could be on google
152+
// retain aws error message
153+
msg := err.Error()
154+
loc, err = resolveGcpZone()
155+
if err != nil {
156+
// return both aws and google error messages
157+
return "", errors.Wrap(err, msg)
158+
}
159+
}
160+
return loc, nil
161+
}
162+
163+
func resolveAwsRegion() (string, error) {
164+
client := &http.Client{
165+
Transport: &http.Transport{
166+
Proxy: http.ProxyFromEnvironment,
167+
DialContext: (&net.Dialer{
168+
Timeout: 1 * time.Second,
169+
KeepAlive: 1 * time.Second,
170+
DualStack: true,
171+
}).DialContext,
172+
MaxIdleConns: 1000,
173+
MaxIdleConnsPerHost: 1000,
174+
IdleConnTimeout: 500 * time.Millisecond,
175+
TLSHandshakeTimeout: 500 * time.Millisecond,
176+
ExpectContinueTimeout: 500 * time.Millisecond,
177+
},
178+
}
179+
// maybe we are on an AWS instance and can resolve what region we are in.
180+
// let's try it out and if we timeout we'll return an error.
181+
// use this url: http://169.254.169.254/latest/dynamic/instance-identity/document
182+
resp, err := client.Get("http://169.254.169.254/latest/dynamic/instance-identity/document")
183+
if err != nil {
184+
return "", errors.Wrapf(err, "location was not provided, fusera attempted to resolve region but encountered an error, this feature only works when fusera is on an amazon or google instance")
185+
}
186+
if resp.StatusCode != http.StatusOK {
187+
return "", errors.Errorf("issue trying to resolve region, got: %d: %s", resp.StatusCode, resp.Status)
188+
}
189+
var payload struct {
190+
Region string `json:"region"`
191+
}
192+
err = json.NewDecoder(resp.Body).Decode(&payload)
193+
if err != nil {
194+
return "", errors.New("issue trying to resolve region, couldn't decode response from amazon")
195+
}
196+
if payload.Region == "" {
197+
return "", errors.New("issue trying to resolve region, amazon returned empty region")
198+
}
199+
return "s3." + payload.Region, nil
200+
}
201+
202+
func resolveGcpZone() (string, error) {
203+
client := &http.Client{
204+
Transport: &http.Transport{
205+
Proxy: http.ProxyFromEnvironment,
206+
DialContext: (&net.Dialer{
207+
Timeout: 1 * time.Second,
208+
KeepAlive: 1 * time.Second,
209+
DualStack: true,
210+
}).DialContext,
211+
MaxIdleConns: 1000,
212+
MaxIdleConnsPerHost: 1000,
213+
IdleConnTimeout: 500 * time.Millisecond,
214+
TLSHandshakeTimeout: 500 * time.Millisecond,
215+
ExpectContinueTimeout: 500 * time.Millisecond,
216+
},
217+
}
218+
req, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/instance/zone?alt=json", nil)
219+
req.Header.Add("Metadata-Flavor", "Google")
220+
resp, err := client.Do(req)
221+
if err != nil {
222+
return "", errors.Wrapf(err, "location was not provided, fusera attempted to resolve region but encountered an error, this feature only works when fusera is on an amazon or google instance")
223+
}
224+
if resp.StatusCode != http.StatusOK {
225+
return "", errors.Errorf("issue trying to resolve region, got: %d: %s", resp.StatusCode, resp.Status)
226+
}
227+
var payload string
228+
err = json.NewDecoder(resp.Body).Decode(&payload)
229+
if err != nil {
230+
return "", errors.New("issue trying to resolve region, couldn't decode response from google")
231+
}
232+
path := filepath.Base(payload)
233+
if path == "" || len(path) == 1 {
234+
return "", errors.New("issue trying to resolve region, google returned empty region")
235+
}
236+
return "gs." + path, nil
237+
}
238+
145239
func parseHTTPError(code int) error {
146240
switch code {
147241
case 400:
@@ -166,6 +260,85 @@ func parseHTTPError(code int) error {
166260
}
167261
}
168262

263+
var Directory = CloudDirectory{
264+
"gs": map[string]bool{
265+
"US": true,
266+
267+
"us-east1-b": true,
268+
"us-east1-c": true,
269+
"us-east1-d": true,
270+
271+
"us-east4-a": true,
272+
"us-east4-b": true,
273+
"us-east4-c": true,
274+
275+
"us-central1-a": true,
276+
"us-central1-b": true,
277+
"us-central1-c": true,
278+
"us-central1-f": true,
279+
280+
"us-west1-a": true,
281+
"us-west1-b": true,
282+
"us-west1-c": true,
283+
},
284+
"s3": map[string]bool{
285+
"us-east-1": true,
286+
},
287+
}
288+
289+
type CloudDirectory map[string]map[string]bool
290+
291+
var IncorrectLocationMessage = `
292+
================
293+
gs.[region]
294+
================
295+
296+
regions for gs:
297+
----------------
298+
299+
US
300+
301+
us-east1-b us-east1-c us-east1-d
302+
303+
us-east4-a us-east4-b us-east4-c
304+
305+
us-central1-a us-central1-b us-central1-c us-central1-f
306+
307+
us-west1-a us-west1-b us-west1-c
308+
309+
================
310+
s3.[region]
311+
================
312+
313+
regions for s3:
314+
----------------
315+
316+
us-east-1
317+
318+
================
319+
For accessing files on ncbi, use the location ftp-ncbi
320+
================
321+
322+
`
323+
324+
func IsLocation(loc string) bool {
325+
ll := strings.Split(loc, ".")
326+
if len(ll) != 2 {
327+
if loc == "ftp-ncbi" {
328+
return true
329+
}
330+
return false
331+
}
332+
regions, ok := Directory[ll[0]]
333+
if !ok {
334+
return false
335+
}
336+
if _, ok := regions[ll[1]]; !ok {
337+
return false
338+
}
339+
return true
340+
}
341+
169342
func String(s string) *string {
170343
return &s
171344
}

cmd/fusera/flags.go

+4-58
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717
package main
1818

1919
import (
20-
"encoding/json"
2120
"fmt"
2221
"io"
2322
"io/ioutil"
24-
"net/http"
2523
"os"
2624
"os/user"
2725
"strconv"
@@ -114,9 +112,6 @@ func NewApp() (app *cli.App, cmd *Commands) {
114112
Aliases: []string{"m"},
115113
Usage: "to mount a folder",
116114
Action: func(c *cli.Context) error {
117-
// if c.IsSet("help") {
118-
// cli.ShowAppHelpAndExit(c, 0)
119-
// }
120115
cmd.IsMount = true
121116
twig.SetDebug(c.IsSet("debug"))
122117
// Populate and parse flags.
@@ -310,15 +305,14 @@ func PopulateMountFlags(c *cli.Context) (ret *Flags, err error) {
310305
}
311306
loc := c.String("loc")
312307
if !c.IsSet("loc") {
313-
// Attempt to resolve the location on aws or gs.
314-
loc, err = resolveLocation()
308+
loc, err = awsutil.ResolveRegion()
315309
if err != nil {
316310
return nil, err
317311
}
318312
}
319-
loc, err = parseLocation(loc)
320-
if err != nil {
321-
return nil, err
313+
ok := awsutil.IsLocation(loc)
314+
if !ok {
315+
return nil, errors.Errorf("gave location of %s, location must match one of these possibilities:\n%s", loc, awsutil.IncorrectLocationMessage)
322316
}
323317
f.Loc = loc
324318

@@ -334,54 +328,6 @@ func PopulateMountFlags(c *cli.Context) (ret *Flags, err error) {
334328
return f, nil
335329
}
336330

337-
func resolveLocation() (string, error) {
338-
// maybe we are on an AWS instance and can resolve what region we are in.
339-
// let's try it out and if we timeout we'll return an error.
340-
// use this url: http://169.254.169.254/latest/dynamic/instance-identity/document
341-
resp, err := http.Get("http://169.254.169.254/latest/dynamic/instance-identity/document")
342-
if err != nil {
343-
return "", errors.Wrapf(err, "location was not provided, fusera attempted to resolve region but encountered an error, this feature only works when fusera is on an amazon instance")
344-
}
345-
if resp.StatusCode != http.StatusOK {
346-
return "", errors.Errorf("issue trying to resolve region, got: %d: %s", resp.StatusCode, resp.Status)
347-
}
348-
var payload struct {
349-
Region string `json:"region"`
350-
}
351-
err = json.NewDecoder(resp.Body).Decode(&payload)
352-
if err != nil {
353-
return "", errors.New("issue trying to resolve region, couldn't decode response from amazon")
354-
}
355-
if payload.Region == "" {
356-
return "", errors.New("issue trying to resolve region, amazon returned empty region")
357-
}
358-
return "s3." + payload.Region, nil
359-
}
360-
361-
func parseLocation(loc string) (string, error) {
362-
ll := strings.Split(loc, ".")
363-
if len(ll) != 2 {
364-
if loc == "ftp-ncbi" {
365-
return loc, nil
366-
}
367-
return "", errors.New("location must be either: gs.US, s3.us-east-1, or ftp-ncbi")
368-
}
369-
if ll[0] != "gs" && ll[0] != "s3" {
370-
return "", errors.Errorf("the service %s is not supported, please use gs or s3", ll[0])
371-
}
372-
if ll[0] == "gs" {
373-
if ll[1] != "US" {
374-
return "", errors.Errorf("the region %s isn't supported on gs, only US", ll[1])
375-
}
376-
}
377-
if ll[0] == "s3" {
378-
if ll[1] != "us-east-1" {
379-
return "", errors.Errorf("the region %s isn't supported on s3, only us-east-1", ll[1])
380-
}
381-
}
382-
return loc, nil
383-
}
384-
385331
func MassageMountFlags(args []string) (ret []string) {
386332
if len(args) == 5 && args[3] == "-o" {
387333
// looks like it's coming from fstab!

cmd/fusera/main.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func main() {
127127
app, cmd := NewApp()
128128
err := app.Run(MassageMountFlags(os.Args))
129129
if err != nil {
130-
fmt.Printf("parsing arguments failed, please review the help with -h:\n %s\n", err.Error())
130+
fmt.Println("starting fusera with given arguments failed, please review the help with -h")
131131
twig.Debugf("%+v\n", err)
132132
os.Exit(1)
133133
}
@@ -140,15 +140,11 @@ func main() {
140140
fmt.Println("Fusera failed to mount the file system")
141141
if strings.Contains(err.Error(), "no such file or directory") {
142142
fmt.Println("It seems like the directory you want to mount to does not exist or you do not have correct permissions to access it. Please create the directory or correct the permissions on it before trying again.")
143-
twig.Debugf("%+v\n", err)
144-
os.Exit(1)
145143
}
146144
if strings.Contains(err.Error(), "EOF") {
147145
fmt.Println("It seems like the directory you want to mount to is already mounted by fusera or another device. Choose another directory or try using the unmount command before trying again. Be considerate of the unmount command, if anything is using the device mounted while attempting to unmount, it will fail.")
148-
twig.Debugf("%+v\n", err)
149-
os.Exit(1)
150146
}
151-
fmt.Printf("%s\n", err.Error())
147+
fmt.Println("Details: " + err.Error())
152148
twig.Debugf("%+v\n", err)
153149
os.Exit(1)
154150
}

0 commit comments

Comments
 (0)