go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/appengine/coordinator/coordinatorTest/logStream.go (about) 1 // Copyright 2015 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 coordinatorTest 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "time" 22 23 "google.golang.org/protobuf/proto" 24 "google.golang.org/protobuf/types/known/durationpb" 25 "google.golang.org/protobuf/types/known/timestamppb" 26 27 "go.chromium.org/luci/common/clock" 28 ds "go.chromium.org/luci/gae/service/datastore" 29 "go.chromium.org/luci/logdog/api/logpb" 30 "go.chromium.org/luci/logdog/appengine/coordinator" 31 "go.chromium.org/luci/logdog/common/types" 32 "go.chromium.org/luci/server/auth/realms" 33 ) 34 35 // TestSecret returns a testing types.StreamPrefix. 36 func TestSecret() types.PrefixSecret { 37 return types.PrefixSecret(bytes.Repeat([]byte{0x6F}, types.PrefixSecretLength)) 38 } 39 40 // TestStream returns a testing stream. 41 type TestStream struct { 42 // Project is the project name for this stream. 43 Project string 44 // Realm is the realm name within the project for this stream. 45 Realm string 46 // Path is the path of this stream. 47 Path types.StreamPath 48 49 // Desc is the log stream descriptor. 50 Desc *logpb.LogStreamDescriptor 51 52 // Prefix is the Coordinator LogPrefix entity. 53 Prefix *coordinator.LogPrefix 54 // Prefix is the Coordinator LogStreamState entity. 55 State *coordinator.LogStreamState 56 // Prefix is the Coordinator LogStream entity. 57 Stream *coordinator.LogStream 58 } 59 60 // MakeStream builds a new TestStream with the supplied parameters. 61 func MakeStream(c context.Context, project, realm string, path types.StreamPath) *TestStream { 62 prefix, name := path.Split() 63 64 now := clock.Now(c).UTC() 65 secret := TestSecret() 66 67 ts := TestStream{ 68 Project: project, 69 Realm: realm, 70 Prefix: &coordinator.LogPrefix{ 71 ID: "", // Filled in by Reload. 72 Created: ds.RoundTime(now), 73 Prefix: "", // Filled in by Reload. 74 Realm: "", // Filled in by Reload. 75 Source: []string{"test suite"}, 76 Expiration: ds.RoundTime(now.Add(24 * time.Hour)), 77 Secret: secret, 78 }, 79 Desc: &logpb.LogStreamDescriptor{ 80 Prefix: string(prefix), 81 Name: string(name), 82 StreamType: logpb.StreamType_TEXT, 83 ContentType: "application/text", 84 Timestamp: timestamppb.New(now), 85 }, 86 State: &coordinator.LogStreamState{ 87 Parent: nil, // Filled in by Reload. 88 Created: ds.RoundTime(now), 89 Updated: time.Time{}, // Filled in by Reload. 90 ExpireAt: ds.RoundTime(now.Add(coordinator.LogStreamStateExpiry)), 91 Secret: secret, 92 TerminalIndex: -1, 93 }, 94 Stream: &coordinator.LogStream{ 95 ID: "", // Filled in by Reload. 96 ProtoVersion: logpb.Version, 97 Created: ds.RoundTime(now), 98 ExpireAt: ds.RoundTime(now.Add(coordinator.LogStreamExpiry)), 99 // Descriptor-derived fields filled in by Reload. 100 }, 101 } 102 ts.Reload(c) 103 return &ts 104 } 105 106 // Reload loads derived fields from their base fields. 107 func (ts *TestStream) Reload(c context.Context) { 108 ts.Path = ts.Desc.Path() 109 110 // LogPrefix 111 ts.Prefix.Prefix = ts.Desc.Prefix 112 ts.Prefix.ID = coordinator.LogPrefixID(types.StreamName(ts.Prefix.Prefix)) 113 if ts.Realm != "" { 114 ts.Prefix.Realm = realms.Join(ts.Project, ts.Realm) 115 } else { 116 ts.Prefix.Realm = "" 117 } 118 119 // LogStream 120 ts.Stream.ID = coordinator.LogStreamID(ts.Path) 121 if err := ts.Stream.LoadDescriptor(ts.Desc); err != nil { 122 panic(err) 123 } 124 125 // LogStreamState 126 ts.State.Updated = ds.RoundTime(clock.Now(c)).UTC() 127 ts.WithProjectNamespace(c, func(c context.Context) { 128 ts.State.Parent = ds.KeyForObj(c, ts.Stream) 129 }) 130 } 131 132 // DescBytes returns the marshalled descriptor bytes. 133 func (ts *TestStream) DescBytes() []byte { 134 v, err := proto.Marshal(ts.Desc) 135 if err != nil { 136 panic(err) 137 } 138 return v 139 } 140 141 // Put adds all of the entities for this TestStream to the datastore. 142 func (ts *TestStream) Put(c context.Context) (err error) { 143 ts.WithProjectNamespace(c, func(c context.Context) { 144 err = ds.Put(c, ts.Prefix, ts.State, ts.Stream) 145 }) 146 return 147 } 148 149 // Get reloads all of the entities for this TestStream. 150 func (ts *TestStream) Get(c context.Context) (err error) { 151 ts.WithProjectNamespace(c, func(c context.Context) { 152 err = ds.Get(c, ts.Prefix, ts.State, ts.Stream) 153 }) 154 return 155 } 156 157 // LogEntry generates a generic testing log entry for this stream with the 158 // specific log stream index. 159 func (ts *TestStream) LogEntry(c context.Context, i int) *logpb.LogEntry { 160 le := logpb.LogEntry{ 161 TimeOffset: durationpb.New(clock.Now(c).Sub(ts.Stream.Created)), 162 StreamIndex: uint64(i), 163 } 164 165 message := fmt.Sprintf("log entry #%d", i) 166 switch ts.Desc.StreamType { 167 case logpb.StreamType_TEXT: 168 le.Content = &logpb.LogEntry_Text{ 169 Text: &logpb.Text{ 170 Lines: []*logpb.Text_Line{ 171 { 172 Value: []byte(message), 173 Delimiter: "\n", 174 }, 175 }, 176 }, 177 } 178 179 case logpb.StreamType_BINARY: 180 le.Content = &logpb.LogEntry_Binary{ 181 Binary: &logpb.Binary{ 182 Data: []byte(message), 183 }, 184 } 185 186 case logpb.StreamType_DATAGRAM: 187 le.Content = &logpb.LogEntry_Datagram{ 188 Datagram: &logpb.Datagram{ 189 Data: []byte(message), 190 }, 191 } 192 } 193 return &le 194 } 195 196 // WithProjectNamespace runs f in proj's namespace, bypassing authentication 197 // checks. 198 func (ts *TestStream) WithProjectNamespace(c context.Context, f func(context.Context)) { 199 WithProjectNamespace(c, ts.Project, f) 200 }