func (sc *Client) GetGcsFileContent(gcsObject *storage.ObjectHandle) ([]byte, error) {
	reader, err := gcsObject.NewReader(sc.Ctx)
	if err != nil {
		return nil, daisy.Errf("Error getting Cloud Storage file content: %v", err)
	}
	return ioutil.ReadAll(reader)
}
GetGcsFileContent returns content of a GCS object as byte array
package storage
import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strings"
	"cloud.google.com/go/storage"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/domain"
	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/utils/logging"
	"github.com/GoogleCloudPlatform/compute-image-tools/daisy"
)
var (
	bucketNameRegex   = `[a-z0-9][-_.a-z0-9]*`
	gsPathRegex       = regexp.MustCompile(fmt.Sprintf(`^gs: 
	slashCounterRegex = regexp.MustCompile("/")
)
type Client struct {
	StorageClient *storage.Client
	Logger        logging.Logger
	Ctx           context.Context
	Oic           domain.ObjectIteratorCreatorInterface
	Soc           domain.StorageObjectCreatorInterface
}
func NewStorageClient(ctx context.Context,
	logger logging.Logger, option ...option.ClientOption) (*Client, error) {
	client, err := storage.NewClient(ctx, option...)
	if err != nil {
		return nil, daisy.Errf("error creating storage client: %v", err)
	}
	sc := &Client{StorageClient: client, Ctx: ctx,
		Oic: &ObjectIteratorCreator{ctx: ctx, sc: client}, Logger: logger}
	sc.Soc = &storageObjectCreator{ctx: ctx, sc: client}
	return sc, nil
}
func (sc *Client) CreateBucket(
	bucketName string, project string, attrs *storage.BucketAttrs) error {
	if err := sc.StorageClient.Bucket(bucketName).Create(sc.Ctx, project, attrs); err != nil {
		return daisy.Errf("Error creating bucket `%v` in project `%v`: %v", bucketName, project, err)
	}
	return nil
}
func (sc *Client) Buckets(projectID string) *storage.BucketIterator {
	return sc.StorageClient.Buckets(sc.Ctx, projectID)
}
func (sc *Client) GetBucket(bucket string) *storage.BucketHandle {
	return sc.StorageClient.Bucket(bucket)
}
func (sc *Client) GetBucketAttrs(bucket string) (*storage.BucketAttrs, error) {
	bucketAttrs, err := sc.StorageClient.Bucket(bucket).Attrs(sc.Ctx)
	if err != nil {
		return nil, daisy.Errf("Error getting bucket attributes for bucket `%v`: %v", bucket, err)
	}
	return bucketAttrs, nil
}
func (sc *Client) GetObject(bucket string, objectPath string) domain.StorageObject {
	return sc.Soc.GetObject(bucket, objectPath)
}
func (sc *Client) GetObjects(bucket string, objectPath string) domain.ObjectIteratorInterface {
	return sc.Oic.CreateObjectIterator(bucket, objectPath)
}
func (sc *Client) GetObjectAttrs(bucket string, objectPath string) (*storage.ObjectAttrs, error) {
	objectAttrs, err := sc.StorageClient.Bucket(bucket).Object(objectPath).Attrs(sc.Ctx)
	if err != nil {
		return nil, daisy.Errf("Error getting object attributes for object `%v\\%v`: %v", bucket, objectPath, err)
	}
	return objectAttrs, nil
}
func (sc *Client) DeleteGcsPath(gcsPath string) error {
	bucketName, objectPath, err := SplitGCSPath(gcsPath)
	if err != nil {
		return err
	}
	log.Printf("Deleting content of: %v", gcsPath)
	it := sc.GetObjects(bucketName, objectPath)
	for {
		attrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return daisy.Errf("Error deleting Cloud Storage path `%v`: %v", gcsPath, err)
		}
		sc.Logger.User(fmt.Sprintf("Deleting gs://%v/%v", bucketName, attrs.Name))
		if err := sc.GetObject(bucketName, attrs.Name).Delete(); err != nil {
			return daisy.Errf("Error deleting Cloud Storage object `%v` in bucket `%v`: %v", attrs.Name, bucketName, err)
		}
	}
	return nil
}
func (sc *Client) DeleteObject(gcsPath string) error {
	bucketName, objectPath, err := SplitGCSPath(gcsPath)
	if err != nil {
		return daisy.Errf("Error deleting `%v`: `%v`", gcsPath, err)
	}
	if err := sc.GetObject(bucketName, objectPath).Delete(); err != nil {
		return daisy.Errf("Error deleting `%v`: `%v`", gcsPath, err)
	}
	return nil
}
func (sc *Client) FindGcsFile(gcsDirectoryPath string, fileExtension string) (*storage.ObjectHandle, error) {
	return sc.FindGcsFileDepthLimited(gcsDirectoryPath, fileExtension, -1)
}
func (sc *Client) FindGcsFileDepthLimited(gcsDirectoryPath string, fileExtension string, lookupDepth int) (*storage.ObjectHandle, error) {
	bucketName, lookupPath, err := SplitGCSPath(gcsDirectoryPath)
	if err != nil {
		return nil, err
	}
	it := sc.GetObjects(bucketName, lookupPath)
	for {
		attrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, daisy.Errf("Error finding file with extension `%v` in Cloud Storage directory `%v`: %v", fileExtension, gcsDirectoryPath, err)
		}
		if !isDepthValid(lookupDepth, lookupPath, attrs.Name) {
			continue
		}
		if !strings.HasSuffix(attrs.Name, fileExtension) {
			continue
		}
		sc.Logger.User(fmt.Sprintf("Found gs://%v/%v", bucketName, attrs.Name))
		return sc.GetBucket(bucketName).Object(attrs.Name), nil
	}
	return nil, daisy.Errf(
		"path %v doesn't contain a file with %v extension", gcsDirectoryPath, fileExtension)
}
func isDepthValid(lookupDepth int, lookupPath, objectPath string) bool {
	if lookupDepth <= -1 {
		return true
	}
	if strings.HasSuffix(lookupPath, "/") {
		lookupPath = lookupPath[:len(lookupPath)-1]
	}
	lookupPathDepth := 0
	if len(lookupPath) > 0 {
		lookupPathDepth = 1 + getSlashCount(lookupPath)
	}
	objectDepth := getSlashCount(objectPath)
	return objectDepth-lookupPathDepth <= lookupDepth
}
func getSlashCount(path string) int {
	return len(slashCounterRegex.FindAllStringIndex(path, -1))
}