diff --git a/cmd/gateway-gcs.go b/cmd/gateway-gcs.go index 0238d1652..78013e31a 100644 --- a/cmd/gateway-gcs.go +++ b/cmd/gateway-gcs.go @@ -24,7 +24,9 @@ import ( "errors" "fmt" "io" + "io/ioutil" "math" + "os" "regexp" "strings" "time" @@ -73,6 +75,9 @@ const ( // The cleanup routine deletes files older than 2 weeks in minio.sys.tmp gcsMultipartExpiry = time.Hour * 24 * 14 + + // Project ID key in credentials.json + gcsProjectIDKey = "project_id" ) // Stored in gcs.json - Contents of this file is not used anywhere. It can be @@ -257,11 +262,34 @@ type gcsGateway struct { const googleStorageEndpoint = "storage.googleapis.com" +// Returns projectID from the GOOGLE_APPLICATION_CREDENTIALS file. +func gcsParseProjectID(credsFile string) (projectID string, err error) { + contents, err := ioutil.ReadFile(credsFile) + if err != nil { + return projectID, err + } + googleCreds := make(map[string]string) + if err = json.Unmarshal(contents, &googleCreds); err != nil { + return projectID, err + } + return googleCreds[gcsProjectIDKey], err +} + // newGCSGateway returns gcs gatewaylayer func newGCSGateway(projectID string) (GatewayLayer, error) { ctx := context.Background() - err := checkGCSProjectID(ctx, projectID) + var err error + if projectID == "" { + // If project ID is not provided on command line, we figure it out + // from the credentials.json file. + projectID, err = gcsParseProjectID(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")) + if err != nil { + return nil, err + } + } + + err = checkGCSProjectID(ctx, projectID) if err != nil { return nil, err } diff --git a/cmd/gateway-gcs_test.go b/cmd/gateway-gcs_test.go index 9a865973e..84bceb11a 100644 --- a/cmd/gateway-gcs_test.go +++ b/cmd/gateway-gcs_test.go @@ -18,6 +18,8 @@ package cmd import ( "fmt" + "io/ioutil" + "os" "reflect" "testing" @@ -179,3 +181,30 @@ func TestFromMinioClientListBucketResultToV2Info(t *testing.T) { t.Errorf("fromMinioClientListBucketResultToV2Info() = %v, want %v", got, listBucketV2Info) } } + +// Test for gcsParseProjectID +func TestGCSParseProjectID(t *testing.T) { + f, err := ioutil.TempFile("", "") + if err != nil { + t.Error(err) + return + } + defer os.Remove(f.Name()) + defer f.Close() + + contents := ` +{ + "type": "service_account", + "project_id": "miniotesting" +} +` + f.WriteString(contents) + projectID, err := gcsParseProjectID(f.Name()) + if err != nil { + t.Error(err) + return + } + if projectID != "miniotesting" { + t.Errorf(`Expected projectID value to be "miniotesting"`) + } +} diff --git a/cmd/gateway-main.go b/cmd/gateway-main.go index f4802433d..973edcd13 100644 --- a/cmd/gateway-main.go +++ b/cmd/gateway-main.go @@ -99,13 +99,13 @@ const gcsGatewayTemplate = `NAME: {{.HelpName}} - {{.Usage}} USAGE: - {{.HelpName}} {{if .VisibleFlags}}[FLAGS]{{end}} PROJECTID + {{.HelpName}} {{if .VisibleFlags}}[FLAGS]{{end}} [PROJECTID] {{if .VisibleFlags}} FLAGS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} PROJECTID: - GCS project id, there are no defaults this is mandatory. + GCS project-id should be provided if GOOGLE_APPLICATION_CREDENTIALS environmental variable is not set. ENVIRONMENT VARIABLES: ACCESS: @@ -115,6 +115,9 @@ ENVIRONMENT VARIABLES: BROWSER: MINIO_BROWSER: To disable web browser access, set this value to "off". + GCS credentials file: + GOOGLE_APPLICATION_CREDENTIALS: Path to credentials.json + EXAMPLES: 1. Start minio gateway server for GCS backend. $ export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json @@ -319,7 +322,12 @@ func gcsGatewayMain(ctx *cli.Context) { cli.ShowCommandHelpAndExit(ctx, "gcs", 1) } - if !isValidGCSProjectIDFormat(ctx.Args().First()) { + projectID := ctx.Args().First() + if projectID == "" && os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") == "" { + errorIf(errGCSProjectIDNotFound, "project-id should be provided as argument or GOOGLE_APPLICATION_CREDENTIALS should be set with path to credentials.json") + cli.ShowCommandHelpAndExit(ctx, "gcs", 1) + } + if projectID != "" && !isValidGCSProjectIDFormat(projectID) { errorIf(errGCSInvalidProjectID, "Unable to start GCS gateway with %s", ctx.Args().First()) cli.ShowCommandHelpAndExit(ctx, "gcs", 1) }