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  }