-
Notifications
You must be signed in to change notification settings - Fork 1
/
metadata.go
228 lines (204 loc) · 6.79 KB
/
metadata.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package metadata
import (
"fmt"
"io"
"net/http"
"os"
"strings"
"cloud.google.com/go/compute/metadata"
)
// OnGCP is GCP上で動いているかどうかを返す
// GCP上と判断されるか確認したのは以下
// Google App Engine Standard for Go 1.11
// Google Compute Engine
// Google Kubernetes Engine
func OnGCP() bool {
return metadata.OnGCE()
}
// ProjectID is Return current GCP ProjectID
// GCP上で動いている場合は、Project Metadataから取得し、そうでなければ、環境変数から取得する
func ProjectID() (string, error) {
if !OnGCP() {
p := os.Getenv("GOOGLE_CLOUD_PROJECT")
if p != "" {
return p, nil
}
p = os.Getenv("GCLOUD_PROJECT")
if p != "" {
return p, nil
}
return "", NewErrNotFound("project id environment valiable is not found. plz set $GOOGLE_CLOUD_PROJECT", nil, nil)
}
projectID, err := metadata.ProjectID()
if err != nil {
return "", fmt.Errorf("failed get project id from metadata server: %w", err)
}
if projectID == "" {
return "", NewErrNotFound("project id is not found", nil, nil)
}
return projectID, nil
}
// NumericProjectID is Return current Numeric GCP ProjectID
// GCP上で動いている場合は、Project Metadataから取得し、そうでなければ、環境変数から取得する
func NumericProjectID() (string, error) {
if !OnGCP() {
p := os.Getenv("NUMERIC_GOOGLE_CLOUD_PROJECT")
if p != "" {
return p, nil
}
return "", NewErrNotFound("numeric project id environment valiable is not found. plz set $NUMERIC_GOOGLE_CLOUD_PROJECT", nil, nil)
}
numericProjectID, err := metadata.NumericProjectID()
if err != nil {
return "", fmt.Errorf("failed get numeric project id from metadata server: %w", err)
}
return numericProjectID, nil
}
// ServiceAccountEmail is Return current Service Account Email
// GCP上で動いている場合は、Metadataから取得し、そうでなければ、環境変数から取得する
func ServiceAccountEmail() (string, error) {
if !OnGCP() {
return os.Getenv("GCLOUD_SERVICE_ACCOUNT"), nil
}
sa, err := getMetadata("service-accounts/default/email")
if err != nil {
return "", fmt.Errorf("failed get ServiceAccountEmail : %w", err)
}
return string(sa), nil
}
// ServiceAccountName is Return current Service Account Name
// ServiceAccountEmailの@より前の部分を返す
func ServiceAccountName() (string, error) {
sa, err := ServiceAccountEmail()
if err != nil {
return "", err
}
l := strings.Split(string(sa), "@")
if len(l) != 2 {
return "", fmt.Errorf("invalid ServiceAccountEmail. email=%s", sa)
}
return l[0], nil
}
// ServiceAccountID is Return current Service Account ID
// fmt "projects/$PROJECT_ID/serviceAccounts/$SERVICE_ACCOUNT_EMAIL"
func ServiceAccountID() (string, error) {
sa, err := ServiceAccountEmail()
if err != nil {
return "", err
}
pID, err := ProjectID()
if err != nil {
return "", err
}
return fmt.Sprintf("projects/%s/serviceAccounts/%s", pID, sa), nil
}
// Region is Appが動いているRegionを取得する
func Region() (string, error) {
if !OnGCP() {
return os.Getenv("INSTANCE_REGION"), nil
}
zone, err := getMetadata("zone")
if err != nil {
return "", fmt.Errorf("failed get Zone : %w", err)
}
return ExtractionRegion(string(zone))
}
// Zone is Appが動いているZoneを取得する
func Zone() (string, error) {
if !OnGCP() {
return os.Getenv("INSTANCE_ZONE"), nil
}
zone, err := getMetadata("zone")
if err != nil {
return "", fmt.Errorf("failed get Zone : %w", err)
}
return ExtractionZone(string(zone))
}
// ExtractionRegion is Metadata Serverから取得する projects/[NUMERIC_PROJECT_ID]/zones/[ZONE] 形式の文字列から、Region部分を取り出す
func ExtractionRegion(metaZone string) (string, error) {
l := strings.Split(string(metaZone), "/")
if len(l) < 1 {
return "", NewErrInvalidArgument("required format : projects/[NUMERIC_PROJECT_ID]/zones/[ZONE]", map[string]interface{}{"input_argument": metaZone}, nil)
}
v := l[len(l)-1]
if len(v) < 3 {
return "", NewErrInvalidArgument("required format : projects/[NUMERIC_PROJECT_ID]/zones/[ZONE]", map[string]interface{}{"input_argument": metaZone}, nil)
}
v = v[:len(v)-2]
return v, nil
}
// ExtractionZone is Metadata Serverから取得する projects/[NUMERIC_PROJECT_ID]/zones/[ZONE] 形式の文字列から、Zone部分を取り出す
func ExtractionZone(metaZone string) (string, error) {
l := strings.Split(string(metaZone), "/")
if len(l) < 1 {
return "", NewErrInvalidArgument("required format : projects/[NUMERIC_PROJECT_ID]/zones/[ZONE]", map[string]interface{}{"input_argument": metaZone}, nil)
}
return l[len(l)-1], nil
}
// InstanceID is Metadata ServerからInstanceIDを取得する
// /computeMetadata/v1/instance/id
func InstanceID() (string, error) {
if !OnGCP() {
return os.Getenv("INSTANCE_ID"), nil
}
id, err := getMetadata("id")
if err != nil {
return "", fmt.Errorf("failed get instance id : %w", err)
}
return string(id), nil
}
// ServiceAccountDefaultToken is Metadata ServerからServiceAccountDefaultTokenを取得する
// /computeMetadata/v1/instance/service-accounts/default/token
func ServiceAccountDefaultToken() (string, error) {
if !OnGCP() {
return os.Getenv("SERVICE_ACCOUNTS_DEFAULT_TOKEN"), nil
}
v, err := getMetadata("service-accounts/default/token")
if err != nil {
return "", fmt.Errorf("failed get service account default token : %w", err)
}
return string(v), nil
}
// GetInstanceAttribute is Instance Metadataを取得する
// GCP以外で動いている時は、環境変数を取得する
func GetInstanceAttribute(key string) (string, error) {
if !OnGCP() {
return os.Getenv(fmt.Sprintf("INSTANCE_%s", key)), nil
}
v, err := metadata.InstanceAttributeValue(key)
if err != nil {
return "", err
}
return v, nil
}
// GetProjectAttribute is Project Metadataを取得する
// GCP以外で動いている時は、環境変数を取得する
func GetProjectAttribute(key string) (string, error) {
if !OnGCP() {
return os.Getenv(fmt.Sprintf("PROJECT_%s", key)), nil
}
v, err := metadata.ProjectAttributeValue(key)
if err != nil {
return "", err
}
return v, nil
}
func getMetadata(path string) ([]byte, error) {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://metadata.google.internal/computeMetadata/v1/instance/%s", path), nil)
if err != nil {
return nil, fmt.Errorf("failed http.NewRequest. path=%s : %w", path, err)
}
req.Header.Set("Metadata-Flavor", "Google")
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed http.SendReq. path=%s : %w", path, err)
}
b, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("failed read response.Body. path=%s : %w", path, err)
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("metadata server response is %v:%v", res.StatusCode, string(b))
}
return b, nil
}