go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/appengine/coordinator/endpoints/services/terminateStream_test.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 services
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"testing"
    21  
    22  	"go.chromium.org/luci/gae/filter/featureBreaker"
    23  	ds "go.chromium.org/luci/gae/service/datastore"
    24  	"go.chromium.org/luci/gae/service/taskqueue"
    25  
    26  	logdog "go.chromium.org/luci/logdog/api/endpoints/coordinator/services/v1"
    27  	"go.chromium.org/luci/logdog/appengine/coordinator"
    28  	ct "go.chromium.org/luci/logdog/appengine/coordinator/coordinatorTest"
    29  
    30  	. "github.com/smartystreets/goconvey/convey"
    31  
    32  	. "go.chromium.org/luci/common/testing/assertions"
    33  )
    34  
    35  func TestTerminateStream(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	Convey(`With a testing configuration`, t, func() {
    39  		c, env := ct.Install()
    40  
    41  		svr := New(ServerSettings{NumQueues: 2})
    42  
    43  		env.AddProject(c, "proj-foo")
    44  		tls := ct.MakeStream(c, "proj-foo", "some-realm", "testing/+/foo/bar")
    45  
    46  		req := logdog.TerminateStreamRequest{
    47  			Project:       string(tls.Project),
    48  			Id:            string(tls.Stream.ID),
    49  			Secret:        tls.Prefix.Secret,
    50  			TerminalIndex: 1337,
    51  		}
    52  
    53  		// The testable TQ object.
    54  		ts := taskqueue.GetTestable(c)
    55  		ts.CreatePullQueue(RawArchiveQueueName(0))
    56  		ts.CreatePullQueue(RawArchiveQueueName(1))
    57  
    58  		Convey(`Returns Forbidden error if not a service.`, func() {
    59  			_, err := svr.TerminateStream(c, &req)
    60  			So(err, ShouldBeRPCPermissionDenied)
    61  		})
    62  
    63  		Convey(`When logged in as a service`, func() {
    64  			env.ActAsService()
    65  
    66  			Convey(`A non-terminal registered stream, "testing/+/foo/bar"`, func() {
    67  				So(tls.Put(c), ShouldBeNil)
    68  				ds.GetTestable(c).CatchupIndexes()
    69  
    70  				Convey(`Can be marked terminal and schedules an archival mutation.`, func() {
    71  					_, err := svr.TerminateStream(c, &req)
    72  					So(err, ShouldBeRPCOK)
    73  					ds.GetTestable(c).CatchupIndexes()
    74  
    75  					// Reload the state and confirm.
    76  					tls.WithProjectNamespace(c, func(c context.Context) {
    77  						So(ds.Get(c, tls.State), ShouldBeNil)
    78  					})
    79  					So(tls.State.TerminalIndex, ShouldEqual, 1337)
    80  					So(tls.State.Terminated(), ShouldBeTrue)
    81  					So(tls.State.ArchivalState(), ShouldEqual, coordinator.ArchiveTasked)
    82  
    83  					Convey(`Can be marked terminal again (idempotent).`, func() {
    84  						_, err := svr.TerminateStream(c, &req)
    85  						So(err, ShouldBeRPCOK)
    86  
    87  						// Reload state and confirm.
    88  						So(tls.Get(c), ShouldBeNil)
    89  
    90  						So(tls.State.Terminated(), ShouldBeTrue)
    91  						So(tls.State.TerminalIndex, ShouldEqual, 1337)
    92  						So(tls.State.ArchivalState(), ShouldEqual, coordinator.ArchiveTasked)
    93  					})
    94  
    95  					Convey(`Will reject attempts to change the terminal index.`, func() {
    96  						req.TerminalIndex = 1338
    97  						_, err := svr.TerminateStream(c, &req)
    98  						So(err, ShouldBeRPCFailedPrecondition, "Log stream is incompatibly terminated.")
    99  
   100  						// Reload state and confirm.
   101  						So(tls.Get(c), ShouldBeNil)
   102  
   103  						So(tls.State.TerminalIndex, ShouldEqual, 1337)
   104  						So(tls.State.Terminated(), ShouldBeTrue)
   105  						So(tls.State.ArchivalState(), ShouldEqual, coordinator.ArchiveTasked)
   106  					})
   107  
   108  					Convey(`Will reject attempts to clear the terminal index.`, func() {
   109  						req.TerminalIndex = -1
   110  						_, err := svr.TerminateStream(c, &req)
   111  						So(err, ShouldBeRPCInvalidArgument, "Negative terminal index.")
   112  
   113  						// Reload state and confirm.
   114  						So(tls.Get(c), ShouldBeNil)
   115  
   116  						So(tls.State.TerminalIndex, ShouldEqual, 1337)
   117  						So(tls.State.Terminated(), ShouldBeTrue)
   118  						So(tls.State.ArchivalState(), ShouldEqual, coordinator.ArchiveTasked)
   119  					})
   120  				})
   121  
   122  				Convey(`Will return an internal server error if Put() fails.`, func() {
   123  					c, fb := featureBreaker.FilterRDS(c, nil)
   124  					fb.BreakFeatures(errors.New("test error"), "PutMulti")
   125  					_, err := svr.TerminateStream(c, &req)
   126  					So(err, ShouldBeRPCInternal)
   127  				})
   128  
   129  				Convey(`Will return an internal server error if Get() fails.`, func() {
   130  					c, fb := featureBreaker.FilterRDS(c, nil)
   131  					fb.BreakFeatures(errors.New("test error"), "GetMulti")
   132  					_, err := svr.TerminateStream(c, &req)
   133  					So(err, ShouldBeRPCInternal)
   134  				})
   135  
   136  				Convey(`Will return a bad request error if the secret doesn't match.`, func() {
   137  					req.Secret[0] ^= 0xFF
   138  					_, err := svr.TerminateStream(c, &req)
   139  					So(err, ShouldBeRPCInvalidArgument, "Request secret doesn't match the stream secret.")
   140  				})
   141  			})
   142  
   143  			Convey(`Will not try and terminate a stream with an invalid path.`, func() {
   144  				req.Id = "!!!invalid path!!!"
   145  				_, err := svr.TerminateStream(c, &req)
   146  				So(err, ShouldBeRPCInvalidArgument, "Invalid ID")
   147  			})
   148  
   149  			Convey(`Will fail if the stream does not exist.`, func() {
   150  				_, err := svr.TerminateStream(c, &req)
   151  				So(err, ShouldBeRPCNotFound, "log stream doesn't exist")
   152  			})
   153  		})
   154  	})
   155  }