golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/relui/sign/server_test.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sign 6 7 import ( 8 "context" 9 "fmt" 10 "io" 11 "log" 12 "sync" 13 "testing" 14 15 "github.com/google/uuid" 16 "golang.org/x/build/internal/access" 17 "golang.org/x/build/internal/relui/protos" 18 "golang.org/x/net/nettest" 19 "google.golang.org/grpc" 20 "google.golang.org/grpc/codes" 21 "google.golang.org/grpc/credentials/insecure" 22 "google.golang.org/grpc/status" 23 ) 24 25 func TestUpdateSigningStatus(t *testing.T) { 26 ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()) 27 client, server := setupSigningTest(t, ctx) 28 go fakeSigningServerClient(t, ctx, client) 29 wg := &sync.WaitGroup{} 30 wg.Add(3) 31 go signRequestSeries(t, ctx, "request-1", server, BuildGPG, wg) 32 go signRequestSeries(t, ctx, "request-2", server, BuildMacOS, wg) 33 go signRequestSeries(t, ctx, "request-3", server, BuildWindows, wg) 34 wg.Wait() 35 } 36 37 func TestUpdateSigningStatusError(t *testing.T) { 38 t.Run("unauthenticated client", func(t *testing.T) { 39 ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()) 40 client, _ := setupSigningTest(t, ctx) 41 stream, err := client.UpdateSigningStatus(context.Background()) 42 if err != nil { 43 t.Fatalf("client.UpdateSigningStatus(ctx) = %v, %s; want no error", stream, err) 44 } 45 wantCode := codes.Unauthenticated 46 if _, err = stream.Recv(); status.Code(err) != wantCode { 47 t.Fatalf("stream.Recv() = %s, want %s", status.Code(err), wantCode) 48 } 49 }) 50 t.Run("non-existent signing request", func(t *testing.T) { 51 // skipping due to go.dev/issue/54654 52 t.Skip("skipping flaky test. see go.dev/issue/54654") 53 54 ctx := access.FakeContextWithOutgoingIAPAuth(context.Background(), fakeIAP()) 55 client, server := setupSigningTest(t, ctx) 56 go fakeSigningServerClient(t, ctx, client) 57 if _, _, _, err := server.ArtifactSigningStatus(ctx, "not-exist"); err == nil { 58 t.Fatalf("ArtifactSigningStatus(ctx, %q) = _, _, nil; want error", "not-exist") 59 } 60 }) 61 } 62 63 func signRequestSeries(t *testing.T, ctx context.Context, id string, server *SigningServer, buildT BuildType, wg *sync.WaitGroup) { 64 defer wg.Done() 65 const uri = "gs://foo/bar.tar.gz" 66 jobID, err := server.SignArtifact(ctx, buildT, []string{uri}) 67 if err != nil { 68 t.Fatalf("SignArtifact(ctx, %q, %v, %q = %s; want no error", id, buildT, uri, err) 69 } 70 _, _, _, err = server.ArtifactSigningStatus(ctx, jobID) 71 if err != nil { 72 t.Fatalf("ArtifactSigningStatus(%q) = %s; want no error", id, err) 73 } 74 _, _, _, err = server.ArtifactSigningStatus(ctx, jobID) 75 if err != nil { 76 t.Fatalf("ArtifactSigningStatus(%q) = %s; want no error", id, err) 77 } 78 } 79 80 // fakeSigningServerClient is a simple implementation of how we expect the client side to perform. 81 // A signing request initiates a signing job. The first status update should return a status of 82 // running. The second status request will return a status of Completed. 83 func fakeSigningServerClient(t *testing.T, ctx context.Context, client protos.ReleaseServiceClient) { 84 stream, err := client.UpdateSigningStatus(ctx) 85 if err != nil { 86 t.Fatalf("client.UpdateSigningStatus(ctx) = %v, %s; want no error", stream, err) 87 } 88 requests := make(chan *protos.SigningRequest, 10) 89 go func() { 90 defer close(requests) 91 for { 92 in, err := stream.Recv() 93 if err == io.EOF { 94 return 95 } 96 if err != nil && status.Code(err) == codes.Canceled { 97 return 98 } 99 if err != nil { 100 t.Logf("client.stream.Recv() = nil, %s; want no error", err) 101 return 102 } 103 requests <- in 104 } 105 }() 106 jobs := make(map[string]int) // jobID -> request count 107 for request := range requests { 108 resp := &protos.SigningStatus{ 109 MessageId: request.GetMessageId(), 110 StatusOneof: nil, 111 } 112 switch r := request.RequestOneof.(type) { 113 case *protos.SigningRequest_Sign: 114 newJob := uuid.NewString() // Create a new pretend job with an ID. 115 jobs[newJob] = 1 116 resp.StatusOneof = &protos.SigningStatus_Started{ 117 Started: &protos.StatusStarted{ 118 JobId: newJob, 119 }, 120 } 121 case *protos.SigningRequest_Status: 122 id := r.Status.JobId 123 c, ok := jobs[id] 124 if !ok { 125 resp.StatusOneof = &protos.SigningStatus_NotFound{} 126 } else if c == 1 { 127 jobs[id] = 2 128 resp.StatusOneof = &protos.SigningStatus_Running{} 129 } else if c == 2 { 130 delete(jobs, id) 131 resp.StatusOneof = &protos.SigningStatus_Completed{ 132 Completed: &protos.StatusCompleted{ 133 GcsUri: []string{"gs://private-bucket/some-file.tar.gz"}, 134 }, 135 } 136 } 137 } 138 if err := stream.Send(resp); err != nil { 139 log.Fatalf("client.stream.Send(%v) = %s, want no error", resp, err) 140 } 141 } 142 stream.CloseSend() 143 } 144 145 func setupSigningTest(t *testing.T, ctx context.Context) (protos.ReleaseServiceClient, *SigningServer) { 146 lis, err := nettest.NewLocalListener("tcp") 147 if err != nil { 148 t.Fatalf("nettest.NewLocalListener(\"tcp\") = %s; want no error", err) 149 } 150 sopts := access.FakeIAPAuthInterceptorOptions() 151 s := grpc.NewServer(sopts...) 152 ss := NewServer() 153 protos.RegisterReleaseServiceServer(s, ss) 154 go s.Serve(lis) 155 // create GRPC client 156 copts := []grpc.DialOption{ 157 grpc.WithBlock(), 158 grpc.WithTransportCredentials(insecure.NewCredentials()), 159 } 160 conn, err := grpc.DialContext(ctx, lis.Addr().String(), copts...) 161 if err != nil { 162 lis.Close() 163 t.Fatalf("grpc.Dial(%s, %+v) = nil, %s; want no error", lis.Addr().String(), copts, err) 164 } 165 sc := protos.NewReleaseServiceClient(conn) 166 t.Cleanup(func() { 167 conn.Close() 168 s.Stop() 169 lis.Close() 170 }) 171 return sc, ss 172 } 173 174 func fakeAuthContext(ctx context.Context) context.Context { 175 return access.ContextWithIAP(ctx, access.IAPFields{ 176 Email: "accounts.google.com:relui-user@google.com", 177 ID: "accounts.google.com:randomuuidstuff", 178 }) 179 } 180 181 func fakeIAP() access.IAPFields { 182 return fakeIAPWithUser("example", "randomuuidstuff") 183 } 184 185 func fakeIAPWithUser(user string, id string) access.IAPFields { 186 return access.IAPFields{ 187 Email: fmt.Sprintf("accounts.google.com:%s@gmail.com", user), 188 ID: fmt.Sprintf("accounts.google.com:%s", id), 189 } 190 }