11 "cloud.google.com/go/storage"
12 "google.golang.org/api/iterator"
15 // GCSGetter is a Getter implementation that will download a module from
17 type GCSGetter struct {
21 func (g *GCSGetter) ClientMode(u *url.URL) (ClientMode, error) {
25 bucket, object, err := g.parseURL(u)
30 client, err := storage.NewClient(ctx)
34 iter := client.Bucket(bucket).Objects(ctx, &storage.Query{Prefix: object})
36 obj, err := iter.Next()
37 if err != nil && err != iterator.Done {
41 if err == iterator.Done {
44 if strings.HasSuffix(obj.Name, "/") {
45 // A directory matched the prefix search, so this must be a directory
46 return ClientModeDir, nil
47 } else if obj.Name != object {
48 // A file matched the prefix search and doesn't have the same name
49 // as the query, so this must be a directory
50 return ClientModeDir, nil
53 // There are no directories or subdirectories, and if a match was returned,
54 // it was exactly equal to the prefix search. So return File mode
55 return ClientModeFile, nil
58 func (g *GCSGetter) Get(dst string, u *url.URL) error {
62 bucket, object, err := g.parseURL(u)
67 // Remove destination if it already exists
69 if err != nil && !os.IsNotExist(err) {
73 // Remove the destination
74 if err := os.RemoveAll(dst); err != nil {
79 // Create all the parent directories
80 if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
84 client, err := storage.NewClient(ctx)
89 // Iterate through all matching objects.
90 iter := client.Bucket(bucket).Objects(ctx, &storage.Query{Prefix: object})
92 obj, err := iter.Next()
93 if err != nil && err != iterator.Done {
96 if err == iterator.Done {
100 if !strings.HasSuffix(obj.Name, "/") {
101 // Get the object destination path
102 objDst, err := filepath.Rel(object, obj.Name)
106 objDst = filepath.Join(dst, objDst)
107 // Download the matching object.
108 err = g.getObject(ctx, client, objDst, bucket, obj.Name)
117 func (g *GCSGetter) GetFile(dst string, u *url.URL) error {
121 bucket, object, err := g.parseURL(u)
126 client, err := storage.NewClient(ctx)
130 return g.getObject(ctx, client, dst, bucket, object)
133 func (g *GCSGetter) getObject(ctx context.Context, client *storage.Client, dst, bucket, object string) error {
134 rc, err := client.Bucket(bucket).Object(object).NewReader(ctx)
140 // Create all the parent directories
141 if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
145 f, err := os.Create(dst)
151 _, err = Copy(ctx, f, rc)
155 func (g *GCSGetter) parseURL(u *url.URL) (bucket, path string, err error) {
156 if strings.Contains(u.Host, "googleapis.com") {
157 hostParts := strings.Split(u.Host, ".")
158 if len(hostParts) != 3 {
159 err = fmt.Errorf("URL is not a valid GCS URL")
163 pathParts := strings.SplitN(u.Path, "/", 5)
164 if len(pathParts) != 5 {
165 err = fmt.Errorf("URL is not a valid GCS URL")
168 bucket = pathParts[3]