-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgcp.go
181 lines (159 loc) · 4.46 KB
/
gcp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package gcp
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"cloud.google.com/go/storage"
"google.golang.org/api/option"
)
var decryptedKeyFileName = "./" + strconv.Itoa(os.Getpid()) + "-gcloud.json"
// Use: go run gcs-deploy.go -bucket builds.etcdevteam.com -object go-ethereum/$(cat version-base.txt)/geth-classic-$TRAVIS_OS_NAME-$(cat version-app.txt).zip -file geth-classic-linux-14.0.zip -key ./.gcloud.key
// writeToGCP writes (uploads) a file or files to GCP Storage.
// 'object' is the path at which 'file' will be written,
// 'bucket' is the parent directory in which the object will be written.
func writeToGCP(client *storage.Client, bucket, object, file string) error {
ctx := context.Background()
// [START upload_file]
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close()
// Write object to storage, ensuring basename for file/object if exists.
wc := client.Bucket(bucket).Object(object).NewWriter(ctx)
if _, err = io.Copy(wc, f); err != nil {
return err
}
if err := wc.Close(); err != nil {
return err
}
// [END upload_file]
fmt.Printf(`Successfully uploaded:
bucket: %v
object: %v
file: %v
`, bucket, object, file)
return nil
}
// SendToGCP sends a file or files to Google Cloud Provider storage
// using a service account JSON key
func SendToGCP(to, files, key string, gpg bool) error {
to = filepath.Clean(to)
files = filepath.Clean(files)
key = filepath.Clean(key)
// Ensure key file exists.
if _, e := os.Stat(key); e != nil {
return e
}
var arbitraryMap = make(map[string]interface{})
// Read key file
bRead, eRead := ioutil.ReadFile(key)
if eRead != nil {
return eRead
}
// Attempt to unmarshal key file, checks for encryption
e := json.Unmarshal(bRead, &arbitraryMap)
if e != nil {
fmt.Println("key is possibly encryped, attempting to decrypt with $GCP_PASSWD")
passwd := os.Getenv("GCP_PASSWD")
if passwd == "" {
return errors.New("env GCP_PASSWD not set, cannot decrypt")
}
// Assume reading for decoding error is it's encrypted... attempt to decrypt
if gpg {
// use pipe to securly provide password
cmd := exec.Command("gpg", "--batch", "--passphrase-fd", "0", "--decrypt", "--output", decryptedKeyFileName, key)
writer, err := cmd.StdinPipe()
if err != nil {
return err
}
go func() {
defer writer.Close()
io.WriteString(writer, passwd)
}()
err = cmd.Run()
if err != nil {
return err
}
} else {
if decryptError := exec.Command("openssl", "aes-256-cbc", "-pass", "env:GCP_PASSWD", "-in", key, "-out", decryptedKeyFileName, "-d").Run(); decryptError != nil {
return decryptError
}
}
fmt.Println("decrypted key file to: ", decryptedKeyFileName)
key = decryptedKeyFileName
// Only remove *unecrypted* key file
defer func() {
key = filepath.Clean(key)
p, pe := filepath.Abs(key)
if pe != nil {
fmt.Println(pe)
} else {
key = p
}
fmt.Printf(`
removing key: %v
`, key)
if errRm := os.Remove(key); errRm != nil {
fmt.Println(errRm)
}
}()
}
ctx := context.Background()
client, err := storage.NewClient(ctx, option.WithServiceAccountFile(key))
if err != nil {
return err
}
// Use glob to get matching file paths.
globs, e := filepath.Glob(files)
if e != nil {
return e
}
// Ensure there is something to upload
if len(globs) == 0 {
return errors.New("no files matching '-to' pattern were found")
}
// Upload each file
for _, f := range globs {
fi, e := os.Stat(f)
if e != nil {
return e
}
if fi.IsDir() {
fmt.Printf("%s is a directory, continuing", fi.Name())
continue
}
// eg.
// to: builds.etcdevteam.com/go-ethereum/3.5.x
// file: ./dist/geth.zip
//
// Set bucket as first in separator-split path
// eg. builds.etcdevteam.com
bucket := strings.Split(filepath.ToSlash(to), "/")[0]
// Get relative path of 'to' based on 'bucket'
// eg. go-ethereum/3.5.x
object, relError := filepath.Rel(bucket, to)
if relError != nil {
return relError
}
// Append file to 'to' path.
// eg. go-ethereum/3.5.x/geth.zip
deployObject := filepath.Join(object, filepath.Base(f))
// Ensure actual '/' [slash]es are used, because Windows will make them '\' [backslashes]
// and google won't folder-ize the path
deployObject = filepath.ToSlash(deployObject)
// Send it.
if deployError := writeToGCP(client, bucket, deployObject, f); deployError != nil {
return deployError
}
}
return nil
}