This repository has been archived by the owner on Oct 12, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 42
/
cloud_repo_file_utility.go
191 lines (174 loc) · 5.26 KB
/
cloud_repo_file_utility.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
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package file
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/golang/protobuf/proto"
spb "github.com/google/shipshape/shipshape/proto/source_context_proto"
)
const (
oFile = "output.tar.gz"
// TODO(emso): Use an http client and GET instead of curl
getToken = `curl "http://metadata/computeMetadata/v1/instance/service-accounts/default/token" -H "X-Google-Metadata-Request: True"`
getFilesFormat = `curl -o output.tar.gz -H "Authorization: Bearer %v" https://source.developers.google.com/p/%v/archive/%v.tar.gz`
unzipFiles = `tar xzf ` + oFile
defaultRevision = "master"
)
type AccessToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
TokenType string `json:"token_type"`
}
// SetupCloudRepo creates a CloudRepo source context and copies down the corresponding
// cloud repo. The root of the repo is returned together with the source context.
func SetupCloudRepo(project string, revision string,
localRepoBase string, volumeRepoBase string) (*spb.SourceContext, string, error) {
var sourceContext *spb.SourceContext
if project == "" || revision == "" {
return sourceContext, "", fmt.Errorf("Missing project name or revison needed to copy Cloud repo"+
", project: %s, revision: %s", project, revision)
}
sourceContext = &spb.SourceContext{
CloudRepo: &spb.CloudRepoSourceContext{
RepoId: &spb.RepoId{
ProjectRepoId: &spb.ProjectRepoId{
ProjectId: proto.String(project),
},
},
RevisionId: proto.String(revision),
},
}
repo, err := CopyCloudRepo(project, revision, localRepoBase)
if err != nil {
return sourceContext, "", fmt.Errorf("Could not copy down Cloud repo: %v", err)
}
root := strings.Replace(repo, localRepoBase, volumeRepoBase, 1)
return sourceContext, root, nil
}
// CopyCloudRepo copies down the cloud repo specified with project id and
// revision and returns a path to the local repo root.
func CopyCloudRepo(project string, revision string, workspace string) (string, error) {
// Find the root of the revision
return localRoot(project, revision, workspace)
}
func localRoot(project string, revision string, workspace string) (string, error) {
token, err := getAccessToken()
if err != nil {
return "", err
}
return copyRepo(token, project, revision, workspace)
}
func copyRepo(token string, project string, revision string, workspace string) (string, error) {
// Copy the repo over, if it already does not exist
// TODO(supertri): only if it does not already exist
// Within the default workspace dir, create a tmp directory for this new revision.
rdir, err := ioutil.TempDir(workspace, "shipshape")
if err != nil {
return "", fmt.Errorf("Could not create tmpdir: %v", err)
}
log.Printf("Created tmp directory: %v", rdir)
err = os.Chdir(rdir)
if err != nil {
return "", fmt.Errorf("Could not change directory: %v", err)
}
_, err = run(fmt.Sprintf(getFilesFormat, token, project, revision))
if err != nil {
return "", fmt.Errorf("Could not get files: %v", err)
}
_, err = run(unzipFiles)
if err != nil {
return "", fmt.Errorf("Could not unzip files: %v", err)
}
err = os.Remove(oFile)
if err != nil {
return "", fmt.Errorf("Could not remove file: %v", err)
}
files, err := ioutil.ReadDir("./")
if err != nil {
return "", fmt.Errorf("Could not read directory: %v", err)
}
if len(files) > 1 {
return "", fmt.Errorf("More than one top level dir for copied over repo")
} else if len(files) == 0 {
return "", fmt.Errorf("No repo")
}
return filepath.Abs(files[0].Name())
}
func getAccessToken() (string, error) {
tokenJSON, err := run(getToken)
if err != nil {
return "", fmt.Errorf("error getting token: %v", err)
}
var t AccessToken
err = json.Unmarshal(tokenJSON, &t)
if err != nil {
return "", fmt.Errorf("Could not unmarshal token: %v", err)
}
return t.AccessToken, nil
}
func run(arg string) ([]byte, error) {
log.Printf("Running %q", arg)
args := argSplit(arg)
cmd := exec.Command(args[0], args[1:]...)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = os.Stderr
err := cmd.Run()
log.Printf("Command output: %v", string(buf.Bytes()))
if err != nil {
log.Printf("Command failed: %v", err)
}
return buf.Bytes(), err
}
func argSplit(line string) []string {
var args []string
var arg []rune
q := false
e := false
for _, c := range line {
if !q && !e && c == ' ' {
if len(arg) > 0 {
args = append(args, string(arg))
arg = arg[:0]
}
continue
}
if !e && c == '"' {
q = !q
continue
}
if !e && c == '\\' {
e = true
continue
}
if e {
e = false
}
arg = append(arg, c)
}
if len(arg) > 0 {
args = append(args, string(arg))
}
return args
}