| OLD | NEW |
| 1 package model | 1 package model |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "bytes" | 4 "bytes" |
| 5 "fmt" | 5 "fmt" |
| 6 "io" | 6 "io" |
| 7 "io/ioutil" | 7 "io/ioutil" |
| 8 "math" | 8 "math" |
| 9 "time" | 9 "time" |
| 10 | 10 |
| 11 "golang.org/x/net/context" | 11 "golang.org/x/net/context" |
| 12 | 12 |
| 13 "github.com/luci/gae/service/datastore" | 13 "github.com/luci/gae/service/datastore" |
| 14 "github.com/luci/luci-go/common/errors" | 14 "github.com/luci/luci-go/common/errors" |
| 15 "github.com/luci/luci-go/common/logging" | 15 "github.com/luci/luci-go/common/logging" |
| 16 ) | 16 ) |
| 17 | 17 |
| 18 // IsAggregateTestFile returns whether filename is that of an aggregate test fil
e. | 18 // IsAggregateTestFile returns whether filename is that of an aggregate test fil
e. |
| 19 func IsAggregateTestFile(filename string) bool { | 19 func IsAggregateTestFile(filename string) bool { |
| 20 switch filename { | 20 switch filename { |
| 21 case "results.json", "results-small.json": | 21 case "results.json", "results-small.json": |
| 22 return true | 22 return true |
| 23 default: | 23 default: |
| 24 return false | 24 return false |
| 25 } | 25 } |
| 26 } | 26 } |
| 27 | 27 |
| 28 // BuildNum is int64 that is used to handle TestFile datastore records | 28 // BuildNum is int64 that is used to handle TestFile datastore records |
| 29 // with null build_number. The value is >= 0 if the datastore value | 29 // with null build_number. The value is >= 0 if the datastore value |
| 30 // was not null. | 30 // was not null. The value is -1 for null datastore value. |
| 31 type BuildNum int64 | 31 type BuildNum int64 |
| 32 | 32 |
| 33 var _ datastore.PropertyConverter = (*BuildNum)(nil) | 33 var _ datastore.PropertyConverter = (*BuildNum)(nil) |
| 34 | 34 |
| 35 // IsNil returns whether b had null value in datastore. | 35 // IsNil returns whether b had null value in datastore. |
| 36 func (b *BuildNum) IsNil() bool { return *b == -1 } | 36 func (b *BuildNum) IsNil() bool { return *b == -1 } |
| 37 | 37 |
| 38 // ToProperty is for implementing datastore.PropertyConverter. | 38 // ToProperty is for implementing datastore.PropertyConverter. |
| 39 func (b *BuildNum) ToProperty() (p datastore.Property, err error) { | 39 func (b *BuildNum) ToProperty() (p datastore.Property, err error) { |
| 40 if b.IsNil() { | 40 if b.IsNil() { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 58 } | 58 } |
| 59 | 59 |
| 60 // DataEntry represents a DataEntry record. | 60 // DataEntry represents a DataEntry record. |
| 61 type DataEntry struct { | 61 type DataEntry struct { |
| 62 ID int64 `gae:"$id"` | 62 ID int64 `gae:"$id"` |
| 63 Data []byte `gae:"data,noindex"` | 63 Data []byte `gae:"data,noindex"` |
| 64 } | 64 } |
| 65 | 65 |
| 66 // TestFile represents a TestFile record. | 66 // TestFile represents a TestFile record. |
| 67 type TestFile struct { | 67 type TestFile struct { |
| 68 » ID int64 `gae:"$id"` | 68 » ID int64 `gae:"$id"` |
| 69 » BuildNumber BuildNum `gae:"build_number"` | 69 » BuildNumber BuildNum `gae:"build_number"` |
| 70 » Builder string `gae:"builder"` | 70 » Builder string `gae:"builder"` |
| 71 » DataKeys []*datastore.Key `gae:"data_keys,noindex"` | 71 » Master string `gae:"master"` |
| 72 » LastMod time.Time `gae:"date"` | 72 » Name string `gae:"name"` |
| 73 » Master string `gae:"master"` | 73 » TestType string `gae:"test_type"` |
| 74 » Name string `gae:"name"` | 74 |
| 75 » TestType string `gae:"test_type"` | 75 » // DataKeys is the keys to the DataEntry(s) that contain |
| 76 » // the data for this TestFile. |
| 77 » DataKeys []*datastore.Key `gae:"data_keys,noindex"` |
| 78 |
| 79 » // LastMod is the last modified time. |
| 80 » LastMod time.Time `gae:"date"` |
| 76 | 81 |
| 77 // Data is the data in the DataEntry(s) pointed to by DataKeys. | 82 // Data is the data in the DataEntry(s) pointed to by DataKeys. |
| 78 // After loading a TestFile from the datastore, this field is | 83 // After loading a TestFile from the datastore, this field is |
| 79 // only available after GetData is called. To put updated | 84 // only available after GetData is called. To put updated |
| 80 // data in this field to the datastore, call PutData. | 85 // data in this field to the datastore, call PutData. |
| 81 // | 86 // |
| 82 » // Users should typically perform the following sequence of calls | 87 » // Users will typically perform the following sequence of calls |
| 83 // in a transaction: | 88 // in a transaction: |
| 84 // | 89 // |
| 85 » // - datastore.Get(tf) | 90 » // ds = datastore.Get(ctx) |
| 86 » // - tf.GetData() | 91 » // err = ds.Get(tf) |
| 87 » // - tf.PutData() | 92 » // err = tf.GetData(ctx) |
| 88 » // - datastore.Put(tf) | 93 » // // manipulate tf.Data |
| 89 » // - datastore.Delete(tf.OldDataKeys) | 94 » // err = tf.PutData(ctx) |
| 95 » // err = ds.Put(tf) |
| 96 » // err = ds.Delete(tf.OldDataKeys) |
| 90 // | 97 // |
| 91 Data io.Reader `gae:"-,noindex"` | 98 Data io.Reader `gae:"-,noindex"` |
| 92 | 99 |
| 93 // OldDataKeys is the keys of the old DataEntry(s) used | 100 // OldDataKeys is the keys of the old DataEntry(s) used |
| 94 // before putting the new data. This field is set after a | 101 // before putting the new data. This field is set after a |
| 95 // successful call to PutData. | 102 // successful call to PutData. |
| 96 // | 103 // |
| 97 // Users are responsible for deleting the DataEntry(s) | 104 // Users are responsible for deleting the DataEntry(s) |
| 98 // pointed to by these keys if they are no longer needed. | 105 // pointed to by these keys if they are no longer needed. |
| 99 OldDataKeys []*datastore.Key `gae:"-,noindex"` | 106 OldDataKeys []*datastore.Key `gae:"-,noindex"` |
| 100 | 107 |
| 101 » // newDataKeys is unused in this implementation. It is | 108 » // NewDataKeys is UNUSED in this implementation. It is |
| 102 // a remnant of the old Python implementation. | 109 // a remnant of the old Python implementation. |
| 103 » newDataKeys []*datastore.Key `gae:"new_data_keys,noindex"` | 110 » NewDataKeys []*datastore.Key `gae:"new_data_keys,noindex"` |
| 104 } | 111 } |
| 105 | 112 |
| 106 // GetData fetches data from the DataEntry(s) pointed to by tf.DataKeys | 113 // GetData fetches data from the DataEntry(s) pointed to by tf.DataKeys |
| 107 // and sets tf.Data to the fetched data. | 114 // and sets tf.Data to the fetched data. |
| 108 func (tf *TestFile) GetData(c context.Context) error { | 115 func (tf *TestFile) GetData(c context.Context) error { |
| 109 ids := make([]int64, len(tf.DataKeys)) | 116 ids := make([]int64, len(tf.DataKeys)) |
| 110 for i, dk := range tf.DataKeys { | 117 for i, dk := range tf.DataKeys { |
| 111 ids[i] = dk.IntID() // Intentional: ignores the Kind stored in d
k. | 118 ids[i] = dk.IntID() // Intentional: ignores the Kind stored in d
k. |
| 112 } | 119 } |
| 113 ids, err := updatedBlobKeys(c, ids...) | 120 ids, err := updatedBlobKeys(c, ids...) |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 // tf.Data and updates tf.LastMod and tf.DataKeys locally. | 167 // tf.Data and updates tf.LastMod and tf.DataKeys locally. |
| 161 // If the returned error is non-nil, tf will be unmodified, | 168 // If the returned error is non-nil, tf will be unmodified, |
| 162 // except that tf.Data may have been consumed. | 169 // except that tf.Data may have been consumed. |
| 163 func (tf *TestFile) putDataEntries(c context.Context) error { | 170 func (tf *TestFile) putDataEntries(c context.Context) error { |
| 164 // Maximum data entries in a TestFile. | 171 // Maximum data entries in a TestFile. |
| 165 const maxDataEntries = 30 | 172 const maxDataEntries = 30 |
| 166 // 1 megabyte is the maximum allowed blob length. | 173 // 1 megabyte is the maximum allowed blob length. |
| 167 // See https://code.googlesource.com/gocloud/+/master/datastore/prop.go#
29. | 174 // See https://code.googlesource.com/gocloud/+/master/datastore/prop.go#
29. |
| 168 const maxBlobLen = 1 << 20 | 175 const maxBlobLen = 1 << 20 |
| 169 | 176 |
| 170 » // TODO: Read maxBlobLen bytes at a time. See io.LimitedReader. | 177 » // TODO(maybe): Read maxBlobLen bytes at a time. See io.LimitedReader. |
| 171 | 178 |
| 172 data, err := ioutil.ReadAll(tf.Data) | 179 data, err := ioutil.ReadAll(tf.Data) |
| 173 if err != nil { | 180 if err != nil { |
| 174 return err | 181 return err |
| 175 } | 182 } |
| 176 | 183 |
| 177 if len(data) > maxDataEntries*maxBlobLen { | 184 if len(data) > maxDataEntries*maxBlobLen { |
| 178 return fmt.Errorf( | 185 return fmt.Errorf( |
| 179 "model: data too large %d bytes (max allowed %d bytes)", | 186 "model: data too large %d bytes (max allowed %d bytes)", |
| 180 len(data), | 187 len(data), |
| (...skipping 12 matching lines...) Expand all Loading... |
| 193 if err := datastore.Get(c).Put(dataEntries); err != nil { | 200 if err := datastore.Get(c).Put(dataEntries); err != nil { |
| 194 return err | 201 return err |
| 195 } | 202 } |
| 196 | 203 |
| 197 newKeys := make([]*datastore.Key, 0, len(dataEntries)) | 204 newKeys := make([]*datastore.Key, 0, len(dataEntries)) |
| 198 for _, de := range dataEntries { | 205 for _, de := range dataEntries { |
| 199 newKeys = append(newKeys, datastore.Get(c).KeyForObj(&de)) | 206 newKeys = append(newKeys, datastore.Get(c).KeyForObj(&de)) |
| 200 } | 207 } |
| 201 | 208 |
| 202 tf.DataKeys = newKeys | 209 tf.DataKeys = newKeys |
| 203 » tf.LastMod = time.Now() | 210 » tf.LastMod = time.Now().UTC() |
| 204 return nil | 211 return nil |
| 205 } | 212 } |
| 206 | 213 |
| 207 // updatedBlobKeys fetches the updated blob keys for the old keys from their blo
bstore migration | 214 // updatedBlobKeys fetches the updated blob keys for the old keys from their blo
bstore migration |
| 208 // records. len(n) will equal len(old) if there is no error. If len(old)>1 and t
here is an error, | 215 // records. len(n) will equal len(old) if there is no error. If len(old)>1 and t
here is an error, |
| 209 // err will be of type MultiError and the error at index i will correspond to th
e | 216 // err will be of type MultiError and the error at index i will correspond to th
e |
| 210 // error in retrieving the updated blob key for the key at index i in old. A | 217 // error in retrieving the updated blob key for the key at index i in old. A |
| 211 // nil error indicates that all values returned in n are valid. | 218 // nil error indicates that all values returned in n are valid. |
| 212 func updatedBlobKeys(c context.Context, old ...int64) (n []int64, err error) { | 219 func updatedBlobKeys(c context.Context, old ...int64) (n []int64, err error) { |
| 213 ds := datastore.Get(c) | 220 ds := datastore.Get(c) |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 q = q.Order("-date") | 324 q = q.Order("-date") |
| 318 | 325 |
| 319 if p.Limit < 0 || p.Limit > defaultLimit { | 326 if p.Limit < 0 || p.Limit > defaultLimit { |
| 320 q = q.Limit(defaultLimit) | 327 q = q.Limit(defaultLimit) |
| 321 } else { | 328 } else { |
| 322 q = q.Limit(p.Limit) | 329 q = q.Limit(p.Limit) |
| 323 } | 330 } |
| 324 | 331 |
| 325 return q | 332 return q |
| 326 } | 333 } |
| OLD | NEW |