gcs: Check if the given project id argument exists (#4583)

Using GCS resource manager API, check if the provided
project id is already created and associated to the current
user account.
This commit is contained in:
A. Elleuch 2017-06-24 06:10:29 +01:00 committed by Harshavardhana
parent 6b70f429ed
commit eaa41e4086
6 changed files with 7864 additions and 10 deletions

View File

@ -19,9 +19,12 @@ package cmd
import "errors" import "errors"
var ( var (
// ProjectID format is not valid. // Project ID format is not valid.
errGCSInvalidProjectID = errors.New("GCS project id is either empty or invalid") errGCSInvalidProjectID = errors.New("GCS project id is either empty or invalid")
// Project ID not found
errGCSProjectIDNotFound = errors.New("unknown project id")
// Multipart identifier is not in the correct form. // Multipart identifier is not in the correct form.
errGCSNotValidMultipartIdentifier = errors.New("Not a valid multipart identifier") errGCSNotValidMultipartIdentifier = errors.New("Not a valid multipart identifier")
) )

View File

@ -29,7 +29,10 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/oauth2/google"
"cloud.google.com/go/storage" "cloud.google.com/go/storage"
cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
"google.golang.org/api/iterator" "google.golang.org/api/iterator"
@ -176,14 +179,56 @@ func gcsToObjectError(err error, params ...string) error {
// gcsProjectIDRegex defines a valid gcs project id format // gcsProjectIDRegex defines a valid gcs project id format
var gcsProjectIDRegex = regexp.MustCompile("^[a-z][a-z0-9-]{5,29}$") var gcsProjectIDRegex = regexp.MustCompile("^[a-z][a-z0-9-]{5,29}$")
// isValidGCSProjectId - checks if a given project id is valid or not. // isValidGCSProjectIDFormat - checks if a given project id format is valid or not.
// Project IDs must start with a lowercase letter and can have lowercase // Project IDs must start with a lowercase letter and can have lowercase ASCII letters,
// ASCII letters, digits or hyphens. Project IDs must be between 6 and 30 characters. // digits or hyphens. Project IDs must be between 6 and 30 characters.
// Ref: https://cloud.google.com/resource-manager/reference/rest/v1/projects#Project (projectId section) // Ref: https://cloud.google.com/resource-manager/reference/rest/v1/projects#Project (projectId section)
func isValidGCSProjectID(projectID string) bool { func isValidGCSProjectIDFormat(projectID string) bool {
// Checking projectID format
return gcsProjectIDRegex.MatchString(projectID) return gcsProjectIDRegex.MatchString(projectID)
} }
// checkGCSProjectID - checks if the project ID does really exist using resource manager API.
func checkGCSProjectID(ctx context.Context, projectID string) error {
// Check if a project id associated to the current account does really exist
resourceManagerClient, err := google.DefaultClient(ctx, cloudresourcemanager.CloudPlatformReadOnlyScope)
if err != nil {
return err
}
baseSvc, err := cloudresourcemanager.New(resourceManagerClient)
if err != nil {
return err
}
projectSvc := cloudresourcemanager.NewProjectsService(baseSvc)
curPageToken := ""
// Iterate over projects list result pages and immediately return nil when
// the project ID is found.
for {
resp, err := projectSvc.List().PageToken(curPageToken).Context(ctx).Do()
if err != nil {
return fmt.Errorf("Error getting projects list: %s", err.Error())
}
for _, p := range resp.Projects {
if p.ProjectId == projectID {
return nil
}
}
if resp.NextPageToken != "" {
curPageToken = resp.NextPageToken
} else {
break
}
}
return errGCSProjectIDNotFound
}
// gcsGateway - Implements gateway for Minio and GCS compatible object storage servers. // gcsGateway - Implements gateway for Minio and GCS compatible object storage servers.
type gcsGateway struct { type gcsGateway struct {
client *storage.Client client *storage.Client
@ -198,6 +243,11 @@ const googleStorageEndpoint = "storage.googleapis.com"
func newGCSGateway(projectID string) (GatewayLayer, error) { func newGCSGateway(projectID string) (GatewayLayer, error) {
ctx := context.Background() ctx := context.Background()
err := checkGCSProjectID(ctx, projectID)
if err != nil {
return nil, err
}
// Initialize a GCS client. // Initialize a GCS client.
client, err := storage.NewClient(ctx) client, err := storage.NewClient(ctx)
if err != nil { if err != nil {

View File

@ -66,8 +66,8 @@ func TestToGCSPageToken(t *testing.T) {
} }
// TestValidGCSProjectID tests the behavior of isValidGCSProjectID // TestIsValidGCSProjectIDFormat tests isValidGCSProjectIDFormat
func TestValidGCSProjectID(t *testing.T) { func TestValidGCSProjectIDFormat(t *testing.T) {
testCases := []struct { testCases := []struct {
ProjectID string ProjectID string
Valid bool Valid bool
@ -93,8 +93,9 @@ func TestValidGCSProjectID(t *testing.T) {
} }
for i, testCase := range testCases { for i, testCase := range testCases {
if isValidGCSProjectID(testCase.ProjectID) != testCase.Valid { valid := isValidGCSProjectIDFormat(testCase.ProjectID)
t.Errorf("Test %d: Expected %v, got %v", i+1, isValidGCSProjectID(testCase.ProjectID), testCase.Valid) if valid != testCase.Valid {
t.Errorf("Test %d: Expected %v, got %v", i+1, valid, testCase.Valid)
} }
} }
} }

View File

@ -270,7 +270,7 @@ func gcsGatewayMain(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "gcs", 1) cli.ShowCommandHelpAndExit(ctx, "gcs", 1)
} }
if !isValidGCSProjectID(ctx.Args().First()) { if !isValidGCSProjectIDFormat(ctx.Args().First()) {
errorIf(errGCSInvalidProjectID, "Unable to start GCS gateway with %s", ctx.Args().First()) errorIf(errGCSInvalidProjectID, "Unable to start GCS gateway with %s", ctx.Args().First())
cli.ShowCommandHelpAndExit(ctx, "gcs", 1) cli.ShowCommandHelpAndExit(ctx, "gcs", 1)
} }

File diff suppressed because it is too large Load Diff

6
vendor/vendor.json vendored
View File

@ -599,6 +599,12 @@
"revision": "470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4", "revision": "470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4",
"revisionTime": "2017-04-25T18:31:26Z" "revisionTime": "2017-04-25T18:31:26Z"
}, },
{
"checksumSHA1": "M1wpFKbvAwtmAafEAYAvJ5mUlf0=",
"path": "google.golang.org/api/cloudresourcemanager/v1",
"revision": "4a52eba41712e0b12c17258ac017e177abeaf535",
"revisionTime": "2017-06-21T19:15:06Z"
},
{ {
"checksumSHA1": "C7k1pbU/WU4CBoBwA4EBUnV/iek=", "checksumSHA1": "C7k1pbU/WU4CBoBwA4EBUnV/iek=",
"path": "google.golang.org/api/gensupport", "path": "google.golang.org/api/gensupport",