Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The LUCI Authors. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 | |
| 15 package git | |
| 16 | |
| 17 import ( | |
| 18 "bytes" | |
| 19 "compress/gzip" | |
| 20 "encoding/hex" | |
| 21 "fmt" | |
| 22 "io/ioutil" | |
| 23 "regexp" | |
| 24 | |
| 25 "golang.org/x/net/context" | |
| 26 | |
| 27 "github.com/golang/protobuf/proto" | |
| 28 | |
| 29 "github.com/luci/gae/service/memcache" | |
| 30 | |
| 31 "github.com/luci/luci-go/common/api/gitiles" | |
| 32 "github.com/luci/luci-go/common/errors" | |
| 33 "github.com/luci/luci-go/common/logging" | |
| 34 "github.com/luci/luci-go/common/proto/google" | |
| 35 | |
| 36 milo "github.com/luci/luci-go/milo/api/proto" | |
| 37 ) | |
| 38 | |
| 39 var gitHash = regexp.MustCompile("[0-9a-fA-F]{40}") | |
| 40 | |
| 41 // Resolve resolves a commitish to a git commit hash. | |
| 42 // | |
| 43 // This operation will assumed to be either fully local (in the case that | |
| 44 // commitish is already a git-hash-looking-thing), or local to the datastore (in | |
| 45 // the case that commitish is a fully-qualified-ref, ref tables are populated by | |
| 46 // a backend cron). | |
| 47 // | |
| 48 // If commitish is some other pattern (e.g. "HEAD~"), this will return the | |
| 49 // commitish as-is. | |
| 50 // | |
| 51 // `resolved` will be true if `commit` is, in fact, a git hash; if it's false, | |
| 52 // then higher layers should be careful about using it as part of a cache key. | |
| 53 func Resolve(c context.Context, url, commitish string) (commit string, resolved bool, err error) { | |
| 54 if gitHash.MatchString(commitish) { | |
| 55 return commitish, true, nil | |
| 56 } | |
| 57 | |
| 58 // TODO(iannucci): actually do lookup and cache it. Maybe have a backend cron | |
| 59 // which refreshes the entire refs space? | |
| 60 return commitish, false, nil | |
| 61 } | |
| 62 | |
| 63 func protoCache(c context.Context, enabled bool, key string, out proto.Message, get func() error) error { | |
|
Ryan Tseng
2017/07/17 20:25:51
comments plz
Also this is fairly specific and int
iannucci
2017/07/17 21:54:39
Done.
| |
| 64 if !enabled { | |
| 65 return get() | |
| 66 } | |
| 67 | |
| 68 cacheEntry := memcache.NewItem(c, key+"|gz") | |
| 69 // try reading from cache | |
| 70 ok := func() bool { | |
| 71 switch err := memcache.Get(c, cacheEntry); err { | |
| 72 case nil: | |
| 73 r, err := gzip.NewReader(bytes.NewReader(cacheEntry.Valu e())) | |
| 74 if err != nil { | |
| 75 logging.WithError(err).Warningf(c, "making ungzi p reader for memcache entry") | |
| 76 return false | |
| 77 } | |
| 78 | |
| 79 data, err := ioutil.ReadAll(r) | |
| 80 if err != nil { | |
| 81 logging.WithError(err).Warningf(c, "ungziping me mcache entry") | |
| 82 return false | |
| 83 } | |
| 84 | |
| 85 if err := proto.Unmarshal(data, out); err != nil { | |
| 86 logging.WithError(err).Warningf(c, "unmarshallin g cache entry") | |
| 87 return false | |
| 88 } | |
| 89 | |
| 90 return true | |
| 91 case memcache.ErrCacheMiss: | |
| 92 default: | |
| 93 logging.WithError(err).Warningf(c, "memcache lookup") | |
| 94 } | |
| 95 return false | |
| 96 }() | |
| 97 if ok { | |
| 98 return nil | |
| 99 } | |
| 100 | |
| 101 if err := get(); err != nil { | |
| 102 return err | |
| 103 } | |
| 104 | |
| 105 data, err := proto.Marshal(out) | |
| 106 if err != nil { | |
| 107 logging.WithError(err).Warningf(c, "marshaling proto") | |
| 108 return nil | |
| 109 } | |
| 110 | |
| 111 var buf bytes.Buffer | |
| 112 wr := gzip.NewWriter(&buf) | |
| 113 _, _ = wr.Write(data) // err is buffered on the writer till Close | |
|
Ryan Tseng
2017/07/17 20:25:51
might as well skip assignment
iannucci
2017/07/17 21:54:39
Done.
| |
| 114 if err := wr.Close(); err != nil { | |
| 115 logging.WithError(err).Warningf(c, "gziping proto") | |
|
Ryan Tseng
2017/07/17 20:25:51
nit: gzipping
iannucci
2017/07/17 21:54:39
Done.
| |
| 116 return nil | |
| 117 } | |
| 118 | |
| 119 cacheEntry.SetValue(buf.Bytes()) | |
| 120 if err := memcache.Set(c, cacheEntry); err != nil { | |
| 121 logging.WithError(err).Warningf(c, "memcache set") | |
| 122 } | |
| 123 | |
| 124 return nil | |
| 125 } | |
| 126 | |
| 127 // GetHistory makes a (cached) call to gitiles to obtain the ConsoleGitInfo for | |
| 128 // the given url, commitish and limit. | |
| 129 func GetHistory(c context.Context, url, commitish string, limit int) (*milo.Cons oleGitInfo, error) { | |
| 130 commitish, useCache, err := Resolve(c, url, commitish) | |
| 131 if err != nil { | |
| 132 return nil, errors.Annotate(err, "resolving %q", commitish).Err( ) | |
| 133 } | |
| 134 | |
| 135 ret := &milo.ConsoleGitInfo{} | |
| 136 cacheKey := fmt.Sprintf("GetHistory|%s|%s|%d", url, commitish, limit) | |
| 137 err = protoCache(c, useCache, cacheKey, ret, func() error { | |
| 138 rawEntries, err := gitiles.Log(c, url, commitish, limit) | |
| 139 if err != nil { | |
| 140 return errors.Annotate(err, "GetHistory").Err() | |
| 141 } | |
| 142 | |
| 143 ret.Commits = make([]*milo.ConsoleGitInfo_Commit, len(rawEntries )) | |
| 144 | |
| 145 for i, e := range rawEntries { | |
| 146 commit := &milo.ConsoleGitInfo_Commit{} | |
| 147 if commit.Id, err = hex.DecodeString(e.Commit); err != n il { | |
| 148 return errors.Annotate(err, "commit is not hex ( %q)", e.Commit).Err() | |
| 149 } | |
| 150 | |
| 151 commit.AuthorName = e.Author.Name | |
| 152 commit.AuthorEmail = e.Author.Email | |
| 153 | |
| 154 ts, err := e.Committer.GetTime() | |
| 155 if err != nil { | |
| 156 return errors.Annotate(err, "commit time unparsi ble (%q)", e.Committer.Time).Err() | |
| 157 } | |
| 158 | |
| 159 commit.CommitTime = google.NewTimestamp(ts) | |
| 160 commit.Msg = e.Message | |
| 161 | |
| 162 ret.Commits[i] = commit | |
| 163 } | |
| 164 return nil | |
| 165 }) | |
| 166 return ret, err | |
| 167 } | |
| OLD | NEW |