Skip to content

Commit ee978b3

Browse files
committed
internal/coordinator/remote, internal/gomote: add instance alive
This change adds the implementation for gomote instance alive. This endpoint enables the caller to check if an instance is alive. If the instance is alive, it will extend the gomote timeout time. A renew timeout method has been added to the session pool. This differs from the existing keep alive method in that it is an single call to renew the timeout for an instance instead of a continuous renewal of the timeout tied to the lifetime of a context. Updated golang/go#48742 Change-Id: I3b3462407d9f4a02c4e2cea0f14950c8c9f21060 Reviewed-on: https://go-review.googlesource.com/c/build/+/374115 Trust: Carlos Amedee <[email protected]> Run-TryBot: Carlos Amedee <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 5584b55 commit ee978b3

File tree

6 files changed

+225
-85
lines changed

6 files changed

+225
-85
lines changed

internal/coordinator/remote/remote.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,16 @@ func (sp *SessionPool) KeepAlive(ctx context.Context, buildletName string) error
228228
})
229229
return nil
230230
}
231+
232+
// RenewTimeout will renew the remote buildlet session by extending the expiration value.
233+
func (sp *SessionPool) RenewTimeout(buildletName string) error {
234+
sp.mu.Lock()
235+
defer sp.mu.Unlock()
236+
237+
s, ok := sp.m[buildletName]
238+
if !ok {
239+
return fmt.Errorf("remote buildlet does not exist=%s", buildletName)
240+
}
241+
s.renew()
242+
return nil
243+
}

internal/coordinator/remote/remote_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,23 @@ func TestSessionPoolDestroySession(t *testing.T) {
9595
}
9696
}
9797
}
98+
99+
func TestRenewTimeout(t *testing.T) {
100+
sp := NewSessionPool(context.Background())
101+
defer sp.Close()
102+
103+
name := sp.AddSession("accounts.google.com:user-xyz-124", "user-x", "builder", "host", &buildlet.FakeClient{})
104+
if err := sp.RenewTimeout(name); err != nil {
105+
t.Errorf("SessionPool.RenewTimeout(%q) = %s; want no error", name, err)
106+
}
107+
}
108+
109+
func TestRenewTimeoutError(t *testing.T) {
110+
sp := NewSessionPool(context.Background())
111+
defer sp.Close()
112+
113+
name := sp.AddSession("accounts.google.com:user-xyz-124", "user-x", "builder", "host", &buildlet.FakeClient{})
114+
if err := sp.RenewTimeout(name + "-wrong"); err == nil {
115+
t.Errorf("SessionPool.RenewTimeout(%q) = %s; want error", name, err)
116+
}
117+
}

internal/gomote/gomote.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,29 @@ func (s *Server) CreateInstance(req *protos.CreateInstanceRequest, stream protos
139139
}
140140
}
141141

142+
// InstanceAlive will ensure that the gomote instance is still alive and will extend the timeout. The requester must be authenticated.
143+
func (s *Server) InstanceAlive(ctx context.Context, req *protos.InstanceAliveRequest) (*protos.InstanceAliveResponse, error) {
144+
creds, err := access.IAPFromContext(ctx)
145+
if err != nil {
146+
log.Printf("InstanceAlive access.IAPFromContext(ctx) = nil, %s", err)
147+
return nil, status.Errorf(codes.Unauthenticated, "request does not contain the required authentication")
148+
}
149+
if req.GetGomoteId() == "" {
150+
return nil, status.Errorf(codes.InvalidArgument, "invalid gomote ID")
151+
}
152+
session, err := s.buildlets.Session(req.GetGomoteId())
153+
if err != nil {
154+
return nil, status.Errorf(codes.NotFound, "specified gomote instance does not exist")
155+
}
156+
if session.OwnerID != creds.ID {
157+
return nil, status.Errorf(codes.PermissionDenied, "not allowed to modify this gomote session")
158+
}
159+
if err := s.buildlets.RenewTimeout(req.GetGomoteId()); err != nil {
160+
return nil, status.Errorf(codes.Internal, "unable to renew timeout")
161+
}
162+
return &protos.InstanceAliveResponse{}, nil
163+
}
164+
142165
// ListInstances will list the gomote instances owned by the requester. The requester must be authenticated.
143166
func (s *Server) ListInstances(ctx context.Context, req *protos.ListInstancesRequest) (*protos.ListInstancesResponse, error) {
144167
creds, err := access.IAPFromContext(ctx)

internal/gomote/gomote_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,75 @@ func TestCreateInstanceError(t *testing.T) {
157157
}
158158
}
159159

160+
func TestInstanceAlive(t *testing.T) {
161+
client := setupGomoteTest(t, context.Background())
162+
gomoteID := mustCreateInstance(t, client, fakeIAP())
163+
req := &protos.InstanceAliveRequest{
164+
GomoteId: gomoteID,
165+
}
166+
ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP())
167+
got, err := client.InstanceAlive(ctx, req)
168+
if err != nil {
169+
t.Fatalf("client.InstanceAlive(ctx, %v) = %v, %s; want no error", req, got, err)
170+
}
171+
}
172+
173+
func TestInstanceAliveError(t *testing.T) {
174+
// This test will create a gomote instance and attempt to call InstanceAlive.
175+
// If overrideID is set to true, the test will use a diffrent gomoteID than the
176+
// the one created for the test.
177+
testCases := []struct {
178+
desc string
179+
ctx context.Context
180+
overrideID bool
181+
gomoteID string // Used iff overrideID is true.
182+
wantCode codes.Code
183+
}{
184+
{
185+
desc: "unauthenticated request",
186+
ctx: context.Background(),
187+
wantCode: codes.Unauthenticated,
188+
},
189+
{
190+
desc: "missing gomote id",
191+
ctx: access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()),
192+
overrideID: true,
193+
wantCode: codes.InvalidArgument,
194+
},
195+
{
196+
desc: "gomote does not exist",
197+
ctx: access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()),
198+
overrideID: true,
199+
gomoteID: "xyz",
200+
wantCode: codes.NotFound,
201+
},
202+
{
203+
desc: "gomote is not owned by caller",
204+
ctx: access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAPWithUser("user-x", "email-y")),
205+
wantCode: codes.PermissionDenied,
206+
},
207+
}
208+
for _, tc := range testCases {
209+
t.Run(tc.desc, func(t *testing.T) {
210+
client := setupGomoteTest(t, context.Background())
211+
gomoteID := mustCreateInstance(t, client, fakeIAP())
212+
if tc.overrideID {
213+
gomoteID = tc.gomoteID
214+
}
215+
req := &protos.InstanceAliveRequest{
216+
GomoteId: gomoteID,
217+
}
218+
got, err := client.InstanceAlive(tc.ctx, req)
219+
if err != nil && status.Code(err) != tc.wantCode {
220+
t.Fatalf("unexpected error: %s", err)
221+
}
222+
if err == nil {
223+
t.Fatalf("client.InstanceAlive(ctx, %v) = %v, nil; want error", req, got)
224+
}
225+
})
226+
}
227+
}
228+
160229
func TestListInstance(t *testing.T) {
161230
client := setupGomoteTest(t, context.Background())
162231
ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP())

0 commit comments

Comments
 (0)