diff --git a/backend/admin/service.go b/backend/admin/service.go index 6a5bb4df14..ed170f0707 100644 --- a/backend/admin/service.go +++ b/backend/admin/service.go @@ -278,47 +278,58 @@ func (s *Service) GetSchema(ctx context.Context, c *connect.Request[ftlv1.GetSch return connect.NewResponse(sch.Msg), nil } -func (s *Service) ApplyChangeset(ctx context.Context, req *connect.Request[ftlv1.ApplyChangesetRequest]) (*connect.Response[ftlv1.ApplyChangesetResponse], error) { +func (s *Service) ApplyChangeset(ctx context.Context, req *connect.Request[ftlv1.ApplyChangesetRequest], stream *connect.ServerStream[ftlv1.ApplyChangesetResponse]) error { events := s.source.Subscribe(ctx) cs, err := s.schemaClient.CreateChangeset(ctx, connect.NewRequest(&ftlv1.CreateChangesetRequest{ Modules: req.Msg.Modules, ToRemove: req.Msg.ToRemove, })) if err != nil { - return nil, fmt.Errorf("failed to create changeset: %w", err) + return fmt.Errorf("failed to create changeset: %w", err) } key, err := key.ParseChangesetKey(cs.Msg.Changeset) if err != nil { - return nil, fmt.Errorf("failed to parse changeset key: %w", err) + return fmt.Errorf("failed to parse changeset key: %w", err) } changeset := &schemapb.Changeset{ Key: cs.Msg.Changeset, Modules: req.Msg.Modules, ToRemove: req.Msg.ToRemove, } + if err := stream.Send(&ftlv1.ApplyChangesetResponse{ + Changeset: changeset, + }); err != nil { + return fmt.Errorf("failed to send changeset: %w", err) + } for e := range channels.IterContext(ctx, events) { switch event := e.(type) { case *schema.ChangesetFinalizedNotification: if event.Key != key { continue } - return connect.NewResponse(&ftlv1.ApplyChangesetResponse{ + if err := stream.Send(&ftlv1.ApplyChangesetResponse{ Changeset: changeset, - }), nil + }); err != nil { + return fmt.Errorf("failed to send changeset: %w", err) + } + return nil case *schema.ChangesetFailedNotification: if event.Key != key { continue } - return nil, fmt.Errorf("failed to apply changeset: %s", event.Error) + return fmt.Errorf("failed to apply changeset: %s", event.Error) case *schema.ChangesetCommittedNotification: if event.Changeset.Key != key { continue } changeset = event.Changeset.ToProto() // We don't wait for cleanup, just return immediately - return connect.NewResponse(&ftlv1.ApplyChangesetResponse{ + if err := stream.Send(&ftlv1.ApplyChangesetResponse{ Changeset: changeset, - }), nil + }); err != nil { + return fmt.Errorf("failed to send changeset: %w", err) + } + return nil case *schema.ChangesetRollingBackNotification: if event.Changeset.Key != key { continue @@ -328,7 +339,7 @@ func (s *Service) ApplyChangeset(ctx context.Context, req *connect.Request[ftlv1 } } - return nil, fmt.Errorf("failed to apply changeset: context cancelled") + return fmt.Errorf("failed to apply changeset: context cancelled") } func (s *Service) PullSchema(ctx context.Context, req *connect.Request[ftlv1.PullSchemaRequest], resp *connect.ServerStream[ftlv1.PullSchemaResponse]) error { diff --git a/backend/protos/xyz/block/ftl/v1/admin.pb.go b/backend/protos/xyz/block/ftl/v1/admin.pb.go index 37de633264..8a25e68df6 100644 --- a/backend/protos/xyz/block/ftl/v1/admin.pb.go +++ b/backend/protos/xyz/block/ftl/v1/admin.pb.go @@ -2155,7 +2155,7 @@ var file_xyz_block_ftl_v1_admin_proto_rawDesc = string([]byte{ 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x45, 0x41, 0x52, 0x4c, 0x49, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x55, 0x42, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x53, - 0x54, 0x10, 0x02, 0x32, 0x92, 0x10, 0x0a, 0x0c, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x72, + 0x54, 0x10, 0x02, 0x32, 0x94, 0x10, 0x0a, 0x0c, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, @@ -2226,69 +2226,70 @@ var file_xyz_block_ftl_v1_admin_proto_rawDesc = string([]byte{ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x0e, 0x41, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x59, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x22, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x23, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5e, 0x0a, 0x0a, 0x50, - 0x75, 0x6c, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x23, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, - 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x30, 0x01, 0x12, 0x59, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, + 0x22, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5e, 0x0a, + 0x0a, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x23, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x75, 0x6c, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x30, 0x01, 0x12, 0x6c, 0x0a, + 0x11, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x65, 0x74, 0x12, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x30, 0x01, 0x12, 0x6c, 0x0a, 0x11, 0x52, - 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, - 0x12, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, + 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x0d, 0x46, + 0x61, 0x69, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x0d, 0x46, 0x61, 0x69, - 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x78, 0x79, 0x7a, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, - 0x69, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0b, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x2e, 0x78, 0x79, 0x7a, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x25, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x72, - 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x66, 0x66, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x79, - 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x66, 0x66, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x72, 0x74, - 0x65, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x66, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x2f, 0x2e, 0x78, + 0x46, 0x61, 0x69, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, + 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x74, - 0x65, 0x66, 0x61, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x66, 0x66, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x72, - 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, - 0x01, 0x12, 0x65, 0x0a, 0x0e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x72, 0x74, 0x65, 0x66, - 0x61, 0x63, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x72, 0x74, - 0x65, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x3e, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, - 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, - 0x76, 0x31, 0x3b, 0x66, 0x74, 0x6c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x66, 0x66, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x44, 0x69, 0x66, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x2f, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, + 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x0e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x72, 0x74, + 0x65, 0x66, 0x61, 0x63, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, + 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x72, 0x74, 0x65, 0x66, 0x61, 0x63, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x3e, 0x50, 0x01, 0x5a, 0x3a, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, + 0x6c, 0x2f, 0x76, 0x31, 0x3b, 0x66, 0x74, 0x6c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, }) var ( diff --git a/backend/protos/xyz/block/ftl/v1/admin.proto b/backend/protos/xyz/block/ftl/v1/admin.proto index 4fdd0c6509..96ea57e082 100644 --- a/backend/protos/xyz/block/ftl/v1/admin.proto +++ b/backend/protos/xyz/block/ftl/v1/admin.proto @@ -232,7 +232,7 @@ service AdminService { // Creates and applies a changeset, returning the result // This blocks until the changeset has completed - rpc ApplyChangeset(ApplyChangesetRequest) returns (ApplyChangesetResponse); + rpc ApplyChangeset(ApplyChangesetRequest) returns (stream ApplyChangesetResponse); // Get the full schema. rpc GetSchema(GetSchemaRequest) returns (GetSchemaResponse) { diff --git a/backend/protos/xyz/block/ftl/v1/ftlv1connect/admin.connect.go b/backend/protos/xyz/block/ftl/v1/ftlv1connect/admin.connect.go index dd60be0154..3886a96aba 100644 --- a/backend/protos/xyz/block/ftl/v1/ftlv1connect/admin.connect.go +++ b/backend/protos/xyz/block/ftl/v1/ftlv1connect/admin.connect.go @@ -119,7 +119,7 @@ type AdminServiceClient interface { ResetSubscription(context.Context, *connect.Request[v1.ResetSubscriptionRequest]) (*connect.Response[v1.ResetSubscriptionResponse], error) // Creates and applies a changeset, returning the result // This blocks until the changeset has completed - ApplyChangeset(context.Context, *connect.Request[v1.ApplyChangesetRequest]) (*connect.Response[v1.ApplyChangesetResponse], error) + ApplyChangeset(context.Context, *connect.Request[v1.ApplyChangesetRequest]) (*connect.ServerStreamForClient[v1.ApplyChangesetResponse], error) // Get the full schema. GetSchema(context.Context, *connect.Request[v1.GetSchemaRequest]) (*connect.Response[v1.GetSchemaResponse], error) // Pull schema changes from the Schema Service. @@ -350,8 +350,8 @@ func (c *adminServiceClient) ResetSubscription(ctx context.Context, req *connect } // ApplyChangeset calls xyz.block.ftl.v1.AdminService.ApplyChangeset. -func (c *adminServiceClient) ApplyChangeset(ctx context.Context, req *connect.Request[v1.ApplyChangesetRequest]) (*connect.Response[v1.ApplyChangesetResponse], error) { - return c.applyChangeset.CallUnary(ctx, req) +func (c *adminServiceClient) ApplyChangeset(ctx context.Context, req *connect.Request[v1.ApplyChangesetRequest]) (*connect.ServerStreamForClient[v1.ApplyChangesetResponse], error) { + return c.applyChangeset.CallServerStream(ctx, req) } // GetSchema calls xyz.block.ftl.v1.AdminService.GetSchema. @@ -423,7 +423,7 @@ type AdminServiceHandler interface { ResetSubscription(context.Context, *connect.Request[v1.ResetSubscriptionRequest]) (*connect.Response[v1.ResetSubscriptionResponse], error) // Creates and applies a changeset, returning the result // This blocks until the changeset has completed - ApplyChangeset(context.Context, *connect.Request[v1.ApplyChangesetRequest]) (*connect.Response[v1.ApplyChangesetResponse], error) + ApplyChangeset(context.Context, *connect.Request[v1.ApplyChangesetRequest], *connect.ServerStream[v1.ApplyChangesetResponse]) error // Get the full schema. GetSchema(context.Context, *connect.Request[v1.GetSchemaRequest]) (*connect.Response[v1.GetSchemaResponse], error) // Pull schema changes from the Schema Service. @@ -514,7 +514,7 @@ func NewAdminServiceHandler(svc AdminServiceHandler, opts ...connect.HandlerOpti svc.ResetSubscription, opts..., ) - adminServiceApplyChangesetHandler := connect.NewUnaryHandler( + adminServiceApplyChangesetHandler := connect.NewServerStreamHandler( AdminServiceApplyChangesetProcedure, svc.ApplyChangeset, opts..., @@ -662,8 +662,8 @@ func (UnimplementedAdminServiceHandler) ResetSubscription(context.Context, *conn return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.v1.AdminService.ResetSubscription is not implemented")) } -func (UnimplementedAdminServiceHandler) ApplyChangeset(context.Context, *connect.Request[v1.ApplyChangesetRequest]) (*connect.Response[v1.ApplyChangesetResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.v1.AdminService.ApplyChangeset is not implemented")) +func (UnimplementedAdminServiceHandler) ApplyChangeset(context.Context, *connect.Request[v1.ApplyChangesetRequest], *connect.ServerStream[v1.ApplyChangesetResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.v1.AdminService.ApplyChangeset is not implemented")) } func (UnimplementedAdminServiceHandler) GetSchema(context.Context, *connect.Request[v1.GetSchemaRequest]) (*connect.Response[v1.GetSchemaResponse], error) { diff --git a/frontend/console/src/protos/xyz/block/ftl/v1/admin_connect.ts b/frontend/console/src/protos/xyz/block/ftl/v1/admin_connect.ts index b1d5d96129..709d7a1f63 100644 --- a/frontend/console/src/protos/xyz/block/ftl/v1/admin_connect.ts +++ b/frontend/console/src/protos/xyz/block/ftl/v1/admin_connect.ts @@ -160,7 +160,7 @@ export const AdminService = { name: "ApplyChangeset", I: ApplyChangesetRequest, O: ApplyChangesetResponse, - kind: MethodKind.Unary, + kind: MethodKind.ServerStreaming, }, /** * Get the full schema. diff --git a/internal/buildengine/deploy.go b/internal/buildengine/deploy.go index 51ddf13522..fa8e331d66 100644 --- a/internal/buildengine/deploy.go +++ b/internal/buildengine/deploy.go @@ -11,87 +11,592 @@ import ( "strings" "connectrpc.com/connect" + "github.com/alecthomas/types/optional" + "github.com/alecthomas/types/result" "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" + buildenginepb "github.com/block/ftl/backend/protos/xyz/block/ftl/buildengine/v1" + langpb "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1" ftlv1 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" schemapb "github.com/block/ftl/common/protos/xyz/block/ftl/schema/v1" + "github.com/block/ftl/common/reflect" + "github.com/block/ftl/common/schema" "github.com/block/ftl/common/sha256" "github.com/block/ftl/common/slices" + "github.com/block/ftl/internal/key" "github.com/block/ftl/internal/log" "github.com/block/ftl/internal/moduleconfig" "github.com/block/ftl/internal/projectconfig" "github.com/block/ftl/internal/schema/schemaeventsource" ) -type deploymentArtefact struct { - *ftlv1.DeploymentArtefact - localPath string -} - type AdminClient interface { - ApplyChangeset(ctx context.Context, req *connect.Request[ftlv1.ApplyChangesetRequest]) (*connect.Response[ftlv1.ApplyChangesetResponse], error) + ApplyChangeset(ctx context.Context, req *connect.Request[ftlv1.ApplyChangesetRequest]) (*connect.ServerStreamForClient[ftlv1.ApplyChangesetResponse], error) ClusterInfo(ctx context.Context, req *connect.Request[ftlv1.ClusterInfoRequest]) (*connect.Response[ftlv1.ClusterInfoResponse], error) GetArtefactDiffs(ctx context.Context, req *connect.Request[ftlv1.GetArtefactDiffsRequest]) (*connect.Response[ftlv1.GetArtefactDiffsResponse], error) UploadArtefact(ctx context.Context) *connect.ClientStreamForClient[ftlv1.UploadArtefactRequest, ftlv1.UploadArtefactResponse] Ping(ctx context.Context, req *connect.Request[ftlv1.PingRequest]) (*connect.Response[ftlv1.PingResponse], error) } -// Deploy a module to the FTL controller with the given number of replicas. Optionally wait for the deployment to become ready. -func Deploy(ctx context.Context, projectConfig projectconfig.Config, modules []Module, replicas int32, waitForDeployOnline bool, adminClient AdminClient) (err error) { +type DependencyGrapher interface { + Graph(moduleNames ...string) (map[string][]string, error) +} + +type pendingModule struct { + name string + module Module + + schemaPath string + schema *schema.Module +} + +type pendingDeploy struct { + modules map[string]*pendingModule + replicas int32 + + publishInSchema bool + changeset optional.Option[key.Changeset] + err chan error + + waitingForModules map[string]bool + superseded bool + supercededModules []*pendingDeploy +} + +type SchemaUpdatedEvent struct { + schema *schema.Schema + // marks which modules were changed together (ie. in the same changeset or queued together) + updatedModules map[string]bool +} + +// DeployCoordinator manages the deployment of modules through changesets. It ensures that changesets are deployed +// in the correct order and that changesets are not deployed if they are superseded by a newer changeset. +// +// The DeployCoordinator also maintains a schema based on active changesets and queued modules. +// This allows the build engine to build modules against where the schema is moving to. For example if module A is dependant on +// module B and module B builds with a breaking schema change deploy coordinator will put deployment of A into a pending state, +// but publish it as part of the its schema. This allows the build engine to react and build module A against the new schema for module B. +// The DeployCoordinator will then create a changeset of A and B together. +type DeployCoordinator struct { + adminClient AdminClient + schemaSource *schemaeventsource.EventSource + dependencyGrapher DependencyGrapher + + // for publishing deploy events + engineUpdates chan *buildenginepb.EngineEvent + + // deployment queue and state tracking + deploymentQueue chan pendingDeploy + + SchemaUpdates chan SchemaUpdatedEvent +} + +func NewDeployCoordinator(ctx context.Context, adminClient AdminClient, schemaSource *schemaeventsource.EventSource, + dependencyGrapher DependencyGrapher, engineUpdates chan *buildenginepb.EngineEvent) *DeployCoordinator { + c := &DeployCoordinator{ + adminClient: adminClient, + schemaSource: schemaSource, + dependencyGrapher: dependencyGrapher, + engineUpdates: engineUpdates, + deploymentQueue: make(chan pendingDeploy, 128), + SchemaUpdates: make(chan SchemaUpdatedEvent, 128), + } + // Start the deployment queue processor + go c.processEvents(ctx) + + return c +} + +func (c *DeployCoordinator) deploy(ctx context.Context, projConfig projectconfig.Config, modules []Module, replicas int32) error { + for _, module := range modules { + c.engineUpdates <- &buildenginepb.EngineEvent{ + Event: &buildenginepb.EngineEvent_ModuleDeployWaiting{ + ModuleDeployWaiting: &buildenginepb.ModuleDeployWaiting{ + Module: module.Config.Module, + }, + }, + } + } + pendingModules := make(map[string]*pendingModule, len(modules)) + for _, m := range modules { + pendingModules[m.Config.Module] = &pendingModule{ + name: m.Config.Module, + module: m, + schemaPath: projConfig.SchemaPath(m.Config.Module), + } + } + + errChan := make(chan error, 1) + c.deploymentQueue <- pendingDeploy{ + modules: pendingModules, + replicas: replicas, + err: errChan} + select { + case <-ctx.Done(): + return ctx.Err() //nolint:wrapcheck + case err := <-errChan: + return err + } +} + +// processEvents handles the deployment queue and groups pending deployments into changesets +// It also maintains schema updates and constructing a target view of the schema for the build engine. +func (c *DeployCoordinator) processEvents(ctx context.Context) { logger := log.FromContext(ctx) - logger.Debugf("Deploying %v", strings.Join(slices.Map(modules, func(m Module) string { return m.Config.Module }), ", ")) - defer func() { + events := c.schemaSource.Subscribe(ctx) + if !c.schemaSource.Live() { + logger.Debugf("Schema source is not live, skipping initial sync.") + c.SchemaUpdates <- SchemaUpdatedEvent{ + schema: &schema.Schema{ + Modules: []*schema.Module{ + schema.Builtins(), + }, + }, + } + } else { + c.schemaSource.WaitForInitialSync(ctx) + c.SchemaUpdates <- SchemaUpdatedEvent{ + schema: c.schemaSource.CanonicalView(), + } + } + + toDeploy := []*pendingDeploy{} + deploying := []*pendingDeploy{} + + for { + select { + case <-ctx.Done(): + return + + case _d := <-c.deploymentQueue: + deployment := &_d + // Prepare by collecting module schemas and uploading artifacts + err := prepareForDeploy(ctx, deployment.modules, c.adminClient) + if err != nil { + deployment.err <- err + continue + } + + // Check if there are older deployments that are superceded by this one or can be joined with this one + for _, existing := range toDeploy { + for _, mod := range existing.modules { + if _, ok := deployment.modules[mod.name]; ok { + existing.superseded = true + } + } + for mod := range existing.waitingForModules { + if _, ok := deployment.modules[mod]; ok { + existing.superseded = true + } + } + if existing.superseded { + if deployment, err = c.mergePendingDeployment(deployment, existing); err != nil { + // Fail new deployment attempt as it is incompatible with a dependency that is already in the queue + deployment.err <- err + continue + } + } + } + toDeploy = slices.Filter(toDeploy, func(d *pendingDeploy) bool { + return !d.superseded + }) + + // Check for modules that need to be rebuilt for this change to be valid + // Try and deploy, unless there are conflicting changesets this will happen immediately + graph, err := c.dependencyGrapher.Graph() + if err != nil { + log.FromContext(ctx).Errorf(err, "could not build graph to order deployment") + continue + } + + modulesToValidate := []string{} + for module, dependencies := range graph { + if _, ok := slices.Find(dependencies, func(s string) bool { + _, ok := deployment.modules[s] + return ok + }); !ok { + continue + } + + modulesToValidate = append(modulesToValidate, module) + } + deployment.waitingForModules = c.invalidModulesForDeployment(c.schemaSource.CanonicalView(), deployment, modulesToValidate) + if len(deployment.waitingForModules) > 0 { + deployment.publishInSchema = true + } + + if c.tryDeployFromQueue(ctx, deployment, toDeploy, graph) { + if deployment.changeset.Ok() { + deploying = append(deploying, deployment) + } + } else { + // We could not deploy, add to the list of pending deployments + toDeploy = append(toDeploy, deployment) + } + if deployment.publishInSchema { + c.publishUpdatedSchema(ctx, maps.Keys(deployment.modules), toDeploy, deploying) //nolint:exptostd + } + case notification := <-events: + var key key.Changeset + var updatedModules []string + switch e := notification.(type) { + case *schema.ChangesetCommittedNotification: + key = e.Changeset.Key + updatedModules = slices.Map(e.Changeset.Modules, func(m *schema.Module) string { return m.Name }) + + for _, m := range e.Changeset.RemovingModules { + if _, ok := slices.Find(updatedModules, func(s string) bool { return s == m.Name }); ok { + continue + } + c.engineUpdates <- &buildenginepb.EngineEvent{ + Timestamp: timestamppb.Now(), + Event: &buildenginepb.EngineEvent_ModuleRemoved{ + ModuleRemoved: &buildenginepb.ModuleRemoved{ + Module: m.Name, + }, + }, + } + } + case *schema.ChangesetRollingBackNotification: + key = e.Changeset.Key + updatedModules = slices.Map(e.Changeset.Modules, func(m *schema.Module) string { return m.Name }) + default: + continue + } + + tmp := []*pendingDeploy{} + graph, err := c.dependencyGrapher.Graph() + if err != nil { + log.FromContext(ctx).Errorf(err, "could not build graph to order deployment") + continue + } + deploying = slices.Filter(deploying, func(d *pendingDeploy) bool { + if d.changeset != optional.Some(key) { + return true + } + if d.publishInSchema { + // already in published schema + updatedModules = []string{} + } + return false + }) + for _, mod := range toDeploy { + if c.tryDeployFromQueue(ctx, mod, toDeploy, graph) { + if mod.changeset.Ok() { + deploying = append(deploying, mod) + } + continue + } + tmp = append(tmp, mod) + } + toDeploy = tmp + c.publishUpdatedSchema(ctx, updatedModules, toDeploy, deploying) + } + } +} + +func (c *DeployCoordinator) tryDeployFromQueue(ctx context.Context, deployment *pendingDeploy, toDeploy []*pendingDeploy, depGraph map[string][]string) bool { + if len(deployment.waitingForModules) > 0 { + return false + } + sets := c.schemaSource.ActiveChangesets() + modules := map[string]bool{} + depModules := map[string]bool{} + for _, module := range deployment.modules { + modules[module.name] = true + for _, dep := range depGraph[module.name] { + depModules[dep] = true + } + } + for _, cs := range sets { + if cs.State >= schema.ChangesetStateCommitted { + continue + } + for _, mod := range cs.Modules { + if modules[mod.Name] || depModules[mod.Name] { + return false + } + } + } + for _, queued := range toDeploy { + for _, mod := range queued.modules { + // We only check for dependencies here, as we already have de-duped modules + // And we have not been removed from toDeploy at this point so we would find ourself + if depModules[mod.name] { + return false + } + } + } + + // No conflicts, lets deploy + + // Deploy all collected modules + for _, module := range deployment.modules { + c.engineUpdates <- &buildenginepb.EngineEvent{ + Event: &buildenginepb.EngineEvent_ModuleDeployStarted{ + ModuleDeployStarted: &buildenginepb.ModuleDeployStarted{ + Module: module.name, + }, + }, + } + } + + keyChan := make(chan result.Result[key.Changeset], 1) + go func() { + err := deploy(ctx, slices.Map(maps.Values(deployment.modules), func(m *pendingModule) *schema.Module { return m.schema }), c.adminClient, keyChan) //nolint:exptostd if err != nil { - logger.Errorf(err, "Failed to deploy %s", strings.Join(slices.Map(modules, func(m Module) string { return m.Config.Module }), ", ")) + // Handle deployment failure + for _, module := range deployment.modules { + c.engineUpdates <- &buildenginepb.EngineEvent{ + Event: &buildenginepb.EngineEvent_ModuleDeployFailed{ + ModuleDeployFailed: &buildenginepb.ModuleDeployFailed{ + Module: module.name, + Errors: &langpb.ErrorList{ + Errors: errorToLangError(err), + }, + }, + }, + } + } + } else { + // Handle deployment success + for _, module := range deployment.modules { + c.engineUpdates <- &buildenginepb.EngineEvent{ + Event: &buildenginepb.EngineEvent_ModuleDeploySuccess{ + ModuleDeploySuccess: &buildenginepb.ModuleDeploySuccess{ + Module: module.name, + }, + }, + } + } + } + deployment.err <- err + for _, sup := range deployment.supercededModules { + sup.err <- err } }() + if key, ok := (<-keyChan).Get(); ok { + deployment.changeset = optional.Some(key) + } + return true +} + +func (c *DeployCoordinator) mergePendingDeployment(d *pendingDeploy, old *pendingDeploy) (*pendingDeploy, error) { + if d.replicas != old.replicas { + return nil, fmt.Errorf("could not deploy %v with pending deployment of %v: replicas were different %d != %d", maps.Keys(d.modules), maps.Keys(old.modules), d.replicas, old.replicas) //nolint:exptostd + } + out := reflect.DeepCopy(d) + addedModules := []string{} + for _, module := range old.modules { + if _, exists := d.modules[module.name]; exists { + continue + } + out.modules[module.name] = old.modules[module.name] + addedModules = append(addedModules, module.name) + } + if len(addedModules) > 0 { + if invalid := c.invalidModulesForDeployment(c.schemaSource.CanonicalView(), out, addedModules); len(invalid) > 0 { + return nil, fmt.Errorf("could not deploy %v with pending deployment of %v: modules were incompatible %v", maps.Keys(d.modules), maps.Keys(old.modules), maps.Keys(invalid)) //nolint:exptostd + } + } + out.publishInSchema = out.publishInSchema || old.publishInSchema + out.supercededModules = append(d.supercededModules, old) //nolint:gocritic + out.supercededModules = append(d.supercededModules, old.supercededModules...) //nolint:gocritic + return out, nil +} + +func (c *DeployCoordinator) invalidModulesForDeployment(originalSch *schema.Schema, deployment *pendingDeploy, modulesToCheck []string) map[string]bool { + out := map[string]bool{} + sch := &schema.Schema{} + for _, moduleSch := range originalSch.Modules { + if _, ok := deployment.modules[moduleSch.Name]; ok { + continue + } + sch.Modules = append(sch.Modules, reflect.DeepCopy(moduleSch)) + } + for _, m := range deployment.modules { + sch.Modules = append(sch.Modules, m.schema) + } + for _, mod := range modulesToCheck { + depSch, ok := slices.Find(sch.Modules, func(m *schema.Module) bool { + return m.Name == mod + }) + if !ok { + continue + } + if _, err := schema.ValidateModuleInSchema(sch, optional.Some(depSch)); err != nil { + out[mod] = true + } + } + return out +} + +func (c *DeployCoordinator) publishUpdatedSchema(ctx context.Context, updatedModules []string, toDeploy, deploying []*pendingDeploy) { + logger := log.FromContext(ctx) + overridden := map[string]bool{} + toRemove := map[string]bool{} + sch := &schema.Schema{} + for _, d := range append(toDeploy, deploying...) { + if !d.publishInSchema { + continue + } + for _, mod := range d.modules { + if _, ok := overridden[mod.name]; ok { + continue + } + overridden[mod.name] = true + sch.Modules = append(sch.Modules, mod.schema) + } + for mod := range d.waitingForModules { + toRemove[mod] = true + } + } + for _, mod := range c.schemaSource.CanonicalView().Modules { + if _, ok := overridden[mod.Name]; ok { + continue + } + sch.Modules = append(sch.Modules, reflect.DeepCopy(mod)) + } + // remove modules that we need to rebuild so that the schema is valid + for { + foundMoreToRemove := false + for _, mod := range sch.Modules { + if toRemove[mod.Name] { + continue + } + for _, im := range mod.Imports() { + if _, ok := toRemove[im]; ok { + toRemove[mod.Name] = true + foundMoreToRemove = true + break + } + } + } + if !foundMoreToRemove { + break + } + } + sch.Modules = slices.Filter(sch.Modules, func(m *schema.Module) bool { + return !toRemove[m.Name] + }) + + sch, err := sch.Validate() + if err != nil { + logger.Errorf(err, "Deploy coordinator could not publish invalid schema") + return + } + updated := map[string]bool{} + for _, m := range updatedModules { + updated[m] = true + } + c.SchemaUpdates <- SchemaUpdatedEvent{ + schema: sch, + updatedModules: updated, + } +} + +func (c *DeployCoordinator) terminateModuleDeployment(ctx context.Context, module string) error { + logger := log.FromContext(ctx).Module(module).Scope("terminate") + + mod, ok := c.schemaSource.CanonicalView().Module(module).Get() + + if !ok { + return fmt.Errorf("deployment for module %s not found", module) + } + key := mod.Runtime.Deployment.DeploymentKey + + logger.Infof("Terminating deployment %s", key) //nolint:forbidigo + stream, err := c.adminClient.ApplyChangeset(ctx, connect.NewRequest(&ftlv1.ApplyChangesetRequest{ + ToRemove: []string{key.String()}, + })) + if err != nil { + return fmt.Errorf("failed to terminate deployment: %w", err) + } + for stream.Receive() { + // Not interested in progress + } + if err := stream.Err(); err != nil { + return fmt.Errorf("failed to terminate deployment: %w", err) + } + return nil +} + +func prepareForDeploy(ctx context.Context, modules map[string]*pendingModule, adminClient AdminClient) (err error) { uploadGroup := errgroup.Group{} - moduleSchemas := make(chan *schemapb.Module, len(modules)) for _, module := range modules { uploadGroup.Go(func() error { - sch, err := uploadArtefacts(ctx, projectConfig, module, adminClient) + sch, err := uploadArtefacts(ctx, module, adminClient) if err != nil { return err } - moduleSchemas <- sch + module.schema = sch return nil }) } if err := uploadGroup.Wait(); err != nil { return fmt.Errorf("failed to upload artefacts: %w", err) } - close(moduleSchemas) - collectedSchemas := []*schemapb.Module{} - for { - sch, ok := <-moduleSchemas - if !ok { - break + return nil +} + +// Deploy a module to the FTL controller with the given number of replicas. Optionally wait for the deployment to become ready. +func deploy(ctx context.Context, modules []*schema.Module, adminClient AdminClient, receivedKey chan result.Result[key.Changeset]) (err error) { + logger := log.FromContext(ctx) + logger.Debugf("Deploying %v", strings.Join(slices.Map(modules, func(m *schema.Module) string { return m.Name }), ", ")) + changesetKey := optional.Option[key.Changeset]{} + defer func() { + if !changesetKey.Ok() { + receivedKey <- result.Err[key.Changeset](err) } - collectedSchemas = append(collectedSchemas, sch) - } + if err != nil { + logger.Errorf(err, "Failed to deploy %s", strings.Join(slices.Map(modules, func(m *schema.Module) string { return m.Name }), ", ")) + } + }() ctx, closeStream := context.WithCancelCause(ctx) defer closeStream(fmt.Errorf("function is complete: %w", context.Canceled)) - _, err = adminClient.ApplyChangeset(ctx, connect.NewRequest(&ftlv1.ApplyChangesetRequest{ - Modules: collectedSchemas, + stream, err := adminClient.ApplyChangeset(ctx, connect.NewRequest(&ftlv1.ApplyChangesetRequest{ + Modules: slices.Map(modules, func(m *schema.Module) *schemapb.Module { + return m.ToProto() + }), })) if err != nil { return fmt.Errorf("failed to deploy changeset: %w", err) } + + for stream.Receive() { + if !changesetKey.Ok() { + k, err := key.ParseChangesetKey(stream.Msg().Changeset.Key) + if err != nil { + return fmt.Errorf("failed to parse changeset key: %w", err) + } + changesetKey = optional.Some(k) + receivedKey <- result.Ok(k) + } + } + + if err := stream.Err(); err != nil { + return fmt.Errorf("failed to deploy changeset: %w", err) + } return nil } -func uploadArtefacts(ctx context.Context, projectConfig projectconfig.Config, module Module, client AdminClient) (*schemapb.Module, error) { - logger := log.FromContext(ctx).Module(module.Config.Module).Scope("deploy") +type deploymentArtefact struct { + *ftlv1.DeploymentArtefact + localPath string +} + +func uploadArtefacts(ctx context.Context, module *pendingModule, client AdminClient) (*schema.Module, error) { + logger := log.FromContext(ctx).Module(module.name).Scope("deploy") ctx = log.ContextWithLogger(ctx, logger) logger.Debugf("Deploying module") - moduleConfig := module.Config.Abs() - files, err := FindFilesToDeploy(moduleConfig, module.Deploy) + moduleConfig := module.module.Config.Abs() + files, err := findFilesToDeploy(moduleConfig, module.module.Deploy) if err != nil { logger.Errorf(err, "failed to find files in %s", moduleConfig) return nil, err @@ -107,7 +612,7 @@ func uploadArtefacts(ctx context.Context, projectConfig projectconfig.Config, mo return nil, fmt.Errorf("failed to get artefact diffs: %w", err) } - moduleSchema, err := loadProtoSchema(projectConfig, moduleConfig) + moduleSchema, err := loadProtoSchema(moduleConfig, module.schemaPath) if err != nil { return nil, err } @@ -131,7 +636,11 @@ func uploadArtefacts(ctx context.Context, projectConfig projectconfig.Config, mo }, }) } - return moduleSchema, nil + parsedSchema, err := schema.ModuleFromProto(moduleSchema) + if err != nil { + return nil, fmt.Errorf("could not parse schema to upload: %w", err) + } + return parsedSchema, nil } func uploadDeploymentArtefact(ctx context.Context, client AdminClient, file deploymentArtefact) error { @@ -178,28 +687,7 @@ func uploadDeploymentArtefact(ctx context.Context, client AdminClient, file depl return nil } -func terminateModuleDeployment(ctx context.Context, events *schemaeventsource.EventSource, client AdminClient, module string) error { - logger := log.FromContext(ctx).Module(module).Scope("terminate") - - mod, ok := events.CanonicalView().Module(module).Get() - - if !ok { - return fmt.Errorf("deployment for module %s not found", module) - } - key := mod.Runtime.Deployment.DeploymentKey - - logger.Infof("Terminating deployment %s", key) - _, err := client.ApplyChangeset(ctx, connect.NewRequest(&ftlv1.ApplyChangesetRequest{ - ToRemove: []string{key.String()}, - })) - if err != nil { - return fmt.Errorf("failed to kill deployment: %w", err) - } - return nil -} - -func loadProtoSchema(projectConfig projectconfig.Config, config moduleconfig.AbsModuleConfig) (*schemapb.Module, error) { - schPath := projectConfig.SchemaPath(config.Module) +func loadProtoSchema(config moduleconfig.AbsModuleConfig, schPath string) (*schemapb.Module, error) { content, err := os.ReadFile(schPath) if err != nil { return nil, fmt.Errorf("failed to load protobuf schema from %q: %w", schPath, err) @@ -225,8 +713,8 @@ func loadProtoSchema(projectConfig projectconfig.Config, config moduleconfig.Abs return module, nil } -// FindFilesToDeploy returns a list of files to deploy for the given module. -func FindFilesToDeploy(config moduleconfig.AbsModuleConfig, deploy []string) ([]string, error) { +// findFilesToDeploy returns a list of files to deploy for the given module. +func findFilesToDeploy(config moduleconfig.AbsModuleConfig, deploy []string) ([]string, error) { var out []string for _, f := range deploy { file := filepath.Clean(filepath.Join(config.DeployDir, f)) diff --git a/internal/buildengine/engine.go b/internal/buildengine/engine.go index 9a9e0122f8..a60f2e3b7c 100644 --- a/internal/buildengine/engine.go +++ b/internal/buildengine/engine.go @@ -14,6 +14,7 @@ import ( "time" "connectrpc.com/connect" + "github.com/alecthomas/atomic" "github.com/alecthomas/types/optional" "github.com/alecthomas/types/pubsub" "github.com/puzpuzpuz/xsync/v3" @@ -24,10 +25,10 @@ import ( buildenginepb "github.com/block/ftl/backend/protos/xyz/block/ftl/buildengine/v1" langpb "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1" ftlv1 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" + "github.com/block/ftl/common/reflect" "github.com/block/ftl/common/schema" "github.com/block/ftl/common/slices" "github.com/block/ftl/internal/buildengine/languageplugin" - "github.com/block/ftl/internal/channels" "github.com/block/ftl/internal/dev" "github.com/block/ftl/internal/log" "github.com/block/ftl/internal/moduleconfig" @@ -79,31 +80,22 @@ type autoRebuildCompletedEvent struct { schema *schema.Module } -type pendingDeploy struct { - modules []Module - err chan error - replicas int32 - superseded bool - supercededModules []*pendingDeploy -} - func (autoRebuildCompletedEvent) rebuildEvent() {} // Engine for building a set of modules. type Engine struct { - adminClient AdminClient - schemaSource *schemaeventsource.EventSource - moduleMetas *xsync.MapOf[string, moduleMeta] - projectConfig projectconfig.Config - moduleDirs []string - watcher *watch.Watcher // only watches for module toml changes - controllerSchema *xsync.MapOf[string, *schema.Module] - schemaChanges *pubsub.Topic[schema.Notification] - cancel context.CancelCauseFunc - parallelism int - modulesToBuild *xsync.MapOf[string, bool] - buildEnv []string - startTime optional.Option[time.Time] + adminClient AdminClient + deployCoordinator *DeployCoordinator + moduleMetas *xsync.MapOf[string, moduleMeta] + projectConfig projectconfig.Config + moduleDirs []string + watcher *watch.Watcher // only watches for module toml changes + targetSchema atomic.Value[*schema.Schema] + cancel context.CancelCauseFunc + parallelism int + modulesToBuild *xsync.MapOf[string, bool] + buildEnv []string + startTime optional.Option[time.Time] // events coming in from plugins pluginEvents chan languageplugin.PluginEvent @@ -120,10 +112,8 @@ type Engine struct { devModeEndpointUpdates chan dev.LocalEndpoint devMode bool - // deployment queue and state tracking - deploymentQueue chan pendingDeploy - os string - arch string + os string + arch string } type Option func(o *Engine) @@ -172,29 +162,28 @@ func New( options ...Option, ) (*Engine, error) { ctx = log.ContextWithLogger(ctx, log.FromContext(ctx).Scope("build-engine")) + rawEngineUpdates := make(chan *buildenginepb.EngineEvent, 128) + e := &Engine{ adminClient: adminClient, - schemaSource: schemaSource, projectConfig: projectConfig, moduleDirs: moduleDirs, moduleMetas: xsync.NewMapOf[string, moduleMeta](), watcher: watch.NewWatcher(optional.Some(projectConfig.WatchModulesLockPath()), "ftl.toml", "**/*.sql"), - controllerSchema: xsync.NewMapOf[string, *schema.Module](), - schemaChanges: pubsub.New[schema.Notification](), pluginEvents: make(chan languageplugin.PluginEvent, 128), parallelism: runtime.NumCPU(), modulesToBuild: xsync.NewMapOf[string, bool](), rebuildEvents: make(chan rebuildEvent, 128), - rawEngineUpdates: make(chan *buildenginepb.EngineEvent, 128), - deploymentQueue: make(chan pendingDeploy, 128), + rawEngineUpdates: rawEngineUpdates, EngineUpdates: pubsub.New[*buildenginepb.EngineEvent](), arch: runtime.GOARCH, // Default to the local env, we attempt to read these from the cluster later os: runtime.GOOS, } + e.deployCoordinator = NewDeployCoordinator(ctx, adminClient, schemaSource, e, rawEngineUpdates) for _, option := range options { option(e) } - e.controllerSchema.Store("builtin", schema.Builtins()) + ctx, cancel := context.WithCancelCause(ctx) e.cancel = cancel @@ -212,9 +201,6 @@ func New( } }() - // Start the deployment queue processor - go e.processDeploymentQueue(ctx) - configs, err := watch.DiscoverModules(ctx, moduleDirs) if err != nil { return nil, fmt.Errorf("could not find modules: %w", err) @@ -258,70 +244,35 @@ func New( e.arch = info.Msg.Arch } } + // Save initial schema + initialEvent := <-e.deployCoordinator.SchemaUpdates + e.targetSchema.Store(initialEvent.schema) if adminClient == nil { return e, nil } - e.startSchemaSync(ctx) return e, nil } -// Sync module schema changes from the FTL controller, as well as from manual -// updates, and merge them into a single schema map. -func (e *Engine) startSchemaSync(ctx context.Context) { - logger := log.FromContext(ctx) - if !e.schemaSource.Live() { - logger.Debugf("Schema source is not live, skipping initial sync.") - } else { - e.schemaSource.WaitForInitialSync(ctx) - for _, module := range e.schemaSource.CanonicalView().Modules { - e.controllerSchema.Store(module.Name, module) - } - } - - go func() { - events := e.schemaSource.Subscribe(ctx) - for event := range channels.IterContext(ctx, events) { - e.processEvent(event) - } - }() -} - -func (e *Engine) processEvent(event schema.Notification) { - switch event := event.(type) { - case *schema.ChangesetCommittedNotification: - adding := map[string]bool{} - for _, a := range event.Changeset.Modules { - adding[a.Name] = true - } - for _, removed := range event.Changeset.RemovingModules { - // If a module has been explicitly killed we only find out about it here - e.controllerSchema.Delete(removed.Name) - if !adding[removed.Name] { - e.rawEngineUpdates <- &buildenginepb.EngineEvent{ - Timestamp: timestamppb.Now(), - Event: &buildenginepb.EngineEvent_ModuleRemoved{ - ModuleRemoved: &buildenginepb.ModuleRemoved{ - Module: removed.Name, - }, - }, - } - } - } - for _, module := range event.Changeset.Modules { - e.controllerSchema.Store(module.Name, module) - } - default: - - } - e.schemaChanges.Publish(event) -} - // Close stops the Engine's schema sync. func (e *Engine) Close() error { e.cancel(fmt.Errorf("build engine stopped: %w", context.Canceled)) return nil } +func (e *Engine) GetModuleSchema(moduleName string) (*schema.Module, bool) { + sch := e.targetSchema.Load() + if sch == nil { + return nil, false + } + module, ok := slices.Find(sch.Modules, func(m *schema.Module) bool { + return m.Name == moduleName + }) + if !ok { + return nil, false + } + return module, true +} + // Graph returns the dependency graph for the given modules. // // If no modules are provided, the entire graph is returned. An error is returned if @@ -354,7 +305,7 @@ func (e *Engine) buildGraph(moduleName string, out map[string][]string) error { deps = meta.module.Dependencies(AlwaysIncludeBuiltin) } if !foundModule { - if sch, ok := e.controllerSchema.Load(moduleName); ok { + if sch, ok := e.GetModuleSchema(moduleName); ok { foundModule = true deps = append(deps, sch.Imports()...) } @@ -374,8 +325,13 @@ func (e *Engine) buildGraph(moduleName string, out map[string][]string) error { // Import manually imports a schema for a module as if it were retrieved from // the FTL controller. -func (e *Engine) Import(ctx context.Context, schema *schema.Module) { - e.controllerSchema.Store(schema.Name, schema) +func (e *Engine) Import(ctx context.Context, moduleSch *schema.Module) { + sch := reflect.DeepCopy(e.targetSchema.Load()) + sch.Modules = slices.Filter(sch.Modules, func(m *schema.Module) bool { + return m.Name != moduleSch.Name + }) + sch.Modules = append(sch.Modules, moduleSch) + e.targetSchema.Store(sch) } // Build attempts to build all local modules. @@ -414,12 +370,6 @@ func (e *Engine) Dev(ctx context.Context, period time.Duration) error { func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration) error { logger := log.FromContext(ctx) - schemaChanges := make(chan schema.Notification, 128) - e.schemaChanges.Subscribe(schemaChanges) - defer func() { - e.schemaChanges.Unsubscribe(schemaChanges) - }() - watchEvents := make(chan watch.WatchEvent, 128) ctx, cancel := context.WithCancelCause(ctx) topic, err := e.watcher.Watch(ctx, period, e.moduleDirs) @@ -436,16 +386,24 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration // Build and deploy all modules first. _ = e.BuildAndDeploy(ctx, 1, true, false) //nolint:errcheck + // Update schema and set initial module hashes + for { + select { + case event := <-e.deployCoordinator.SchemaUpdates: + e.targetSchema.Store(event.schema) + continue + default: + } + break + } moduleHashes := map[string][]byte{} - e.controllerSchema.Range(func(name string, sch *schema.Module) bool { + for _, sch := range e.targetSchema.Load().Modules { hash, err := computeModuleHash(sch) if err != nil { - logger.Errorf(err, "compute hash for %s failed", name) - return false + return fmt.Errorf("compute hash for %s failed: %w", sch.Name, err) } - moduleHashes[name] = hash - return true - }) + moduleHashes[sch.Name] = hash + } for { select { @@ -474,10 +432,10 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration }, } logger.Debugf("calling build and deploy %q", event.Config.Module) - _ = e.BuildAndDeploy(ctx, 1, true, false, config.Module) //nolint:errcheck + _ = e.BuildAndDeploy(ctx, 1, false, false, config.Module) //nolint:errcheck } case watch.WatchEventModuleRemoved: - err := terminateModuleDeployment(ctx, e.schemaSource, e.adminClient, event.Config.Module) + err := e.deployCoordinator.terminateModuleDeployment(ctx, event.Config.Module) if err != nil { logger.Errorf(err, "terminate %s failed", event.Config.Module) } @@ -518,46 +476,41 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration meta.module.Config = validConfig e.moduleMetas.Store(event.Config.Module, meta) - _ = e.BuildAndDeploy(ctx, 1, true, false, event.Config.Module) //nolint:errcheck + _ = e.BuildAndDeploy(ctx, 1, false, false, event.Config.Module) //nolint:errcheck } - case event := <-schemaChanges: - switch event := event.(type) { - case *schema.ChangesetCommittedNotification: - inCs := map[string]bool{} - for _, module := range event.Changeset.Modules { - inCs[module.Name] = true + case event := <-e.deployCoordinator.SchemaUpdates: + e.targetSchema.Store(event.schema) + for _, module := range event.schema.Modules { + if !event.updatedModules[module.Name] { + continue + } + existingHash, ok := moduleHashes[module.Name] + if !ok { + existingHash = []byte{} } - for _, module := range event.Changeset.Modules { - existingHash, ok := moduleHashes[module.Name] - if !ok { - existingHash = []byte{} - } - hash, err := computeModuleHash(module) - if err != nil { - logger.Errorf(err, "compute hash for %s failed", module.Name) - continue - } + hash, err := computeModuleHash(module) + if err != nil { + logger.Errorf(err, "compute hash for %s failed", module.Name) + continue + } - if bytes.Equal(hash, existingHash) { - logger.Tracef("schema for %s has not changed", module.Name) - continue - } + if bytes.Equal(hash, existingHash) { + logger.Tracef("schema for %s has not changed", module.Name) + continue + } - moduleHashes[module.Name] = hash + moduleHashes[module.Name] = hash - dependentModuleNames := e.getDependentModuleNames(module.Name) - dependentModuleNames = slices.Filter(dependentModuleNames, func(name string) bool { - // We don't update if this was already part of the same changeset - return !inCs[name] - }) - if len(dependentModuleNames) > 0 { - logger.Infof("%s's schema changed; processing %s", module.Name, strings.Join(dependentModuleNames, ", ")) - _ = e.BuildAndDeploy(ctx, 1, true, false, dependentModuleNames...) //nolint:errcheck - } + dependentModuleNames := e.getDependentModuleNames(module.Name) + dependentModuleNames = slices.Filter(dependentModuleNames, func(name string) bool { + // We don't update if this was already part of the same changeset + return !event.updatedModules[name] + }) + if len(dependentModuleNames) > 0 { + logger.Infof("%s's schema changed; processing %s", module.Name, strings.Join(dependentModuleNames, ", ")) //nolint:forbidigo + _ = e.BuildAndDeploy(ctx, 1, false, false, dependentModuleNames...) //nolint:errcheck } - default: - } case event := <-e.rebuildEvents: @@ -571,7 +524,6 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration break readLoop } } - // Batch generate stubs for all auto rebuilds // // This is normally part of each group in the build topology, but auto rebuilds do not go through that flow @@ -608,7 +560,9 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration modulesToDeploy = append(modulesToDeploy, moduleToDeploy.module) } } - _ = e.deploy(ctx, modulesToDeploy, 1) //nolint:errcheck + go func() { + _ = e.deployCoordinator.deploy(ctx, e.projectConfig, modulesToDeploy, 1) //nolint:errcheck + }() } // Batch together all new builds requested @@ -621,7 +575,7 @@ func (e *Engine) watchForModuleChanges(ctx context.Context, period time.Duration modulesToBuild[event.module] = true } if len(modulesToBuild) > 0 { - _ = e.BuildAndDeploy(ctx, 1, true, false, maps.Keys(modulesToBuild)...) //nolint + _ = e.BuildAndDeploy(ctx, 1, false, false, maps.Keys(modulesToBuild)...) //nolint } } } @@ -818,7 +772,6 @@ func computeModuleHash(module *schema.Module) ([]byte, error) { if _, err := hasher.Write(data); err != nil { return nil, err // Handle errors that might occur during the write } - return hasher.Sum(nil), nil } @@ -877,15 +830,31 @@ func (e *Engine) BuildAndDeploy(ctx context.Context, replicas int32, waitForDepl modulesToDeploy = append(modulesToDeploy, module) return nil } - return e.deploy(ctx, []Module{module}, replicas) + deployErr := make(chan error, 1) + go func() { + deployErr <- e.deployCoordinator.deploy(ctx, e.projectConfig, []Module{module}, replicas) + }() + if waitForDeployOnline { + return <-deployErr + } + return nil }, moduleNames...) if buildErr != nil { - return fmt.Errorf("build failed: %w", buildErr) + return buildErr } - if singleChangeset { - // Queue the modules for deployment instead of deploying directly - return e.deploy(ctx, modulesToDeploy, replicas) + deployGroup := &errgroup.Group{} + deployGroup.Go(func() error { + // Wait for all build attempts to complete + if singleChangeset { + // Queue the modules for deployment instead of deploying directly + return e.deployCoordinator.deploy(ctx, e.projectConfig, modulesToDeploy, replicas) + } + return nil + }) + if waitForDeployOnline { + err := deployGroup.Wait() + return err //nolint:wrapcheck } return nil } @@ -1097,7 +1066,7 @@ func (e *Engine) handleDependencyCycleError(ctx context.Context, depErr Dependen "builtin": schema.Builtins(), } for _, dep := range graph[module] { - if sch, ok := e.controllerSchema.Load(dep); ok { + if sch, ok := e.GetModuleSchema(dep); ok { fakeDeps[dep] = sch continue } @@ -1163,7 +1132,7 @@ func (e *Engine) tryBuild(ctx context.Context, mustBuild map[string]bool, module // Publish either the schema from the FTL controller, or from a local build. func (e *Engine) mustSchema(ctx context.Context, moduleName string, builtModules map[string]*schema.Module, schemas chan<- *schema.Module) error { - if sch, ok := e.controllerSchema.Load(moduleName); ok { + if sch, ok := e.GetModuleSchema(moduleName); ok { schemas <- sch return nil } @@ -1252,10 +1221,9 @@ func (e *Engine) gatherSchemas( moduleSchemas map[string]*schema.Module, out map[string]*schema.Module, ) error { - e.controllerSchema.Range(func(name string, sch *schema.Module) bool { - out[name] = sch - return true - }) + for _, sch := range e.targetSchema.Load().Modules { + out[sch.Name] = sch + } e.moduleMetas.Range(func(name string, meta moduleMeta) bool { if _, ok := moduleSchemas[name]; ok { @@ -1272,7 +1240,7 @@ func (e *Engine) gatherSchemas( func (e *Engine) syncNewStubReferences(ctx context.Context, newModules map[string]*schema.Module, metasMap map[string]moduleMeta) error { fullSchema := &schema.Schema{Modules: maps.Values(newModules)} - for _, module := range e.schemaSource.CanonicalView().Modules { + for _, module := range e.targetSchema.Load().Modules { if _, ok := newModules[module.Name]; !ok { fullSchema.Modules = append(fullSchema.Modules, module) } @@ -1442,165 +1410,3 @@ func (e *Engine) watchForPluginEvents(originalCtx context.Context) { } } } - -func (e *Engine) deploy(ctx context.Context, modules []Module, replicas int32) error { - for _, module := range modules { - e.rawEngineUpdates <- &buildenginepb.EngineEvent{ - Event: &buildenginepb.EngineEvent_ModuleDeployWaiting{ - ModuleDeployWaiting: &buildenginepb.ModuleDeployWaiting{ - Module: module.Config.Module, - }, - }, - } - } - errChan := make(chan error, 1) - e.deploymentQueue <- pendingDeploy{modules: modules, replicas: replicas, err: errChan} - select { - case <-ctx.Done(): - return ctx.Err() //nolint:wrapcheck - case err := <-errChan: - return err - } -} - -// processDeploymentQueue handles the deployment queue and groups pending deployments into changesets -func (e *Engine) processDeploymentQueue(ctx context.Context) { - events := e.schemaSource.Subscribe(ctx) - - toDeploy := []*pendingDeploy{} - for { - select { - case <-ctx.Done(): - return - - case deployment := <-e.deploymentQueue: - // Try and deploy, unless there are conflicting changesets this will happen immediately - graph, err := e.Graph() - if err != nil { - log.FromContext(ctx).Errorf(err, "could not build graph to order deployment") - continue - } - if !e.tryDeployFromQueue(ctx, &deployment, toDeploy, graph) { - // We could not deploy, add to the list of pending deployments - // But first check if there are older deployments that are superceded by this one - modules := map[string]bool{} - for _, module := range deployment.modules { - modules[module.Config.Module] = true - } - for _, existing := range toDeploy { - for _, mod := range existing.modules { - if modules[mod.Config.Module] { - existing.superseded = true - deployment.supercededModules = append(deployment.supercededModules, existing) - deployment.supercededModules = append(deployment.supercededModules, existing.supercededModules...) - } - } - } - toDeploy = slices.Filter(toDeploy, func(d *pendingDeploy) bool { - return !d.superseded - }) - toDeploy = append(toDeploy, &deployment) - } - case notification := <-events: - switch notification.(type) { - case *schema.ChangesetCommittedNotification, *schema.ChangesetRollingBackNotification: - tmp := []*pendingDeploy{} - if len(toDeploy) == 0 { - continue - } - graph, err := e.Graph() - if err != nil { - log.FromContext(ctx).Errorf(err, "could not build graph to order deployment") - continue - } - for _, mod := range toDeploy { - if e.tryDeployFromQueue(ctx, mod, toDeploy, graph) { - continue - } - tmp = append(tmp, mod) - } - toDeploy = tmp - default: - - } - } - } -} - -func (e *Engine) tryDeployFromQueue(ctx context.Context, deployment *pendingDeploy, toDeploy []*pendingDeploy, depGraph map[string][]string) bool { - sets := e.schemaSource.ActiveChangesets() - modules := map[string]bool{} - depModules := map[string]bool{} - for _, module := range deployment.modules { - modules[module.Config.Module] = true - for _, dep := range depGraph[module.Config.Module] { - depModules[dep] = true - } - } - for _, cs := range sets { - if cs.State >= schema.ChangesetStateCommitted { - continue - } - for _, mod := range cs.Modules { - if modules[mod.Name] || depModules[mod.Name] { - return false - } - } - } - for _, queued := range toDeploy { - for _, mod := range queued.modules { - // We only check for dependencies here, as we already have de-duped modules - // And we have not been removed from toDeploy at this point so we would find ourself - if depModules[mod.Config.Module] { - return false - } - } - } - - // No conflicts, lets deploy - - // Deploy all collected modules - for _, module := range deployment.modules { - e.rawEngineUpdates <- &buildenginepb.EngineEvent{ - Event: &buildenginepb.EngineEvent_ModuleDeployStarted{ - ModuleDeployStarted: &buildenginepb.ModuleDeployStarted{ - Module: module.Config.Module, - }, - }, - } - } - go func() { - err := Deploy(ctx, e.projectConfig, deployment.modules, deployment.replicas, true, e.adminClient) - if err != nil { - // Handle deployment failure - for _, module := range deployment.modules { - e.rawEngineUpdates <- &buildenginepb.EngineEvent{ - Event: &buildenginepb.EngineEvent_ModuleDeployFailed{ - ModuleDeployFailed: &buildenginepb.ModuleDeployFailed{ - Module: module.Config.Module, - Errors: &langpb.ErrorList{ - Errors: errorToLangError(err), - }, - }, - }, - } - } - } else { - // Handle deployment success - for _, module := range deployment.modules { - e.rawEngineUpdates <- &buildenginepb.EngineEvent{ - Event: &buildenginepb.EngineEvent_ModuleDeploySuccess{ - ModuleDeploySuccess: &buildenginepb.ModuleDeploySuccess{ - Module: module.Config.Module, - }, - }, - } - } - } - deployment.err <- err - for _, sup := range deployment.supercededModules { - sup.err <- err - } - }() - return true -} diff --git a/internal/buildengine/engine_integration_test.go b/internal/buildengine/engine_integration_test.go index 24e7219e61..f81e853f33 100644 --- a/internal/buildengine/engine_integration_test.go +++ b/internal/buildengine/engine_integration_test.go @@ -3,8 +3,11 @@ package buildengine_test import ( + "path/filepath" "testing" + "github.com/alecthomas/assert/v2" + "github.com/block/ftl/common/strcase" in "github.com/block/ftl/internal/integration" ) @@ -33,3 +36,46 @@ func TestInt64BuildError(t *testing.T) { ), ) } + +// Tests how build engine reacts to a module changing its exported verbs. +func TestModuleInterfaceChanges(t *testing.T) { + in.Run(t, + in.WithDevMode(), + // TODO: resolve issue blocking kotlin support in test + in.WithLanguages("go"), + + in.CopyModule("parent"), + in.CopyModule("child"), + + in.WaitForDev(true, "should have no errors at the start"), + + updateVerb("parent", "verb1", "verb2"), + in.WaitForDev(false, "child should fail to build because verb1 is now verb2"), + updateVerb("child", "verb1", "verb2"), + in.WaitForDev(true, "child should succeed because it now uses verb2"), + updateVerb("parent", "verb2", "verb3"), + in.WaitForDev(false, "child should fail to build because verb2 is now verb3"), + updateVerb("parent", "verb3", "verb2"), + in.WaitForDev(true, "child should now build because verb2 is back (verb3 reverted)"), + updateVerb("parent", "verb2", "verb4"), + updateVerb("child", "verb2", "verb4"), + in.WaitForDev(true, "should have no errors after both modules updated to verb4"), + ) +} + +// updateVerb can replace decls or clients +func updateVerb(module, old, new string) in.Action { + return func(t testing.TB, ic in.TestContext) { + var file string + switch ic.Language { + case "go": + file = filepath.Join(".", module, module+".go") + case "kotlin": + file = filepath.Join(".", module, "src", "main", "kotlin", "ftl", module, module+".kt") + } + assert.NotEqual(t, "", file, "unsupported language: %s", ic.Language) + + in.Exec("sed", "-i.bak", "s/"+old+"/"+new+"/g", file)(t, ic) + in.Exec("sed", "-i.bak", "s/"+strcase.ToUpperCamel(old)+"/"+strcase.ToUpperCamel(new)+"/g", file)(t, ic) + } +} diff --git a/internal/buildengine/testdata/go/child/child.go b/internal/buildengine/testdata/go/child/child.go new file mode 100644 index 0000000000..7400d9ba79 --- /dev/null +++ b/internal/buildengine/testdata/go/child/child.go @@ -0,0 +1,21 @@ +package child + +import ( + "context" + "ftl/parent" + + "github.com/block/ftl/go-runtime/ftl" // Import the FTL SDK. +) + +type HelloRequest struct { + Name ftl.Option[string] `json:"name"` +} + +type HelloResponse struct { + Message string `json:"message"` +} + +//ftl:verb export +func Hello(ctx context.Context, req parent.HelloRequest, client parent.Verb1Client) (parent.HelloResponse, error) { + return client(ctx, req) +} diff --git a/internal/buildengine/testdata/go/child/ftl.toml b/internal/buildengine/testdata/go/child/ftl.toml new file mode 100644 index 0000000000..63dd910d8b --- /dev/null +++ b/internal/buildengine/testdata/go/child/ftl.toml @@ -0,0 +1,2 @@ +module = "child" +language = "go" diff --git a/internal/buildengine/testdata/go/child/go.mod b/internal/buildengine/testdata/go/child/go.mod new file mode 100644 index 0000000000..fe4a7fe1d0 --- /dev/null +++ b/internal/buildengine/testdata/go/child/go.mod @@ -0,0 +1,65 @@ +module ftl/child + +go 1.24.0 + +require github.com/block/ftl v0.460.0 + +require ( + al.essio.dev/pkg/shellescape v1.5.1 // indirect + connectrpc.com/connect v1.17.0 // indirect + connectrpc.com/grpcreflect v1.3.0 // indirect + connectrpc.com/otelconnect v0.7.2 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/XSAM/otelsql v0.37.0 // indirect + github.com/alecthomas/atomic v0.1.0-alpha2 // indirect + github.com/alecthomas/concurrency v0.0.2 // indirect + github.com/alecthomas/kong v1.8.1 // indirect + github.com/alecthomas/participle/v2 v2.1.1 // indirect + github.com/alecthomas/types v0.18.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/danieljoos/wincred v1.2.2 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.9.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect + github.com/swaggest/jsonschema-go v0.3.73 // indirect + github.com/swaggest/refl v1.3.0 // indirect + github.com/zalando/go-keyring v0.2.6 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect +) diff --git a/internal/buildengine/testdata/go/child/go.sum b/internal/buildengine/testdata/go/child/go.sum new file mode 100644 index 0000000000..75141eab46 --- /dev/null +++ b/internal/buildengine/testdata/go/child/go.sum @@ -0,0 +1,318 @@ +al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= +al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= +connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= +connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= +connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= +connectrpc.com/otelconnect v0.7.2 h1:WlnwFzaW64dN06JXU+hREPUGeEzpz3Acz2ACOmN8cMI= +connectrpc.com/otelconnect v0.7.2/go.mod h1:JS7XUKfuJs2adhCnXhNHPHLz6oAaZniCJdSF00OZSew= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/IBM/sarama v1.45.1 h1:nY30XqYpqyXOXSNoe2XCgjj9jklGM1Ye94ierUb1jQ0= +github.com/IBM/sarama v1.45.1/go.mod h1:qifDhA3VWSrQ1TjSMyxDl3nYL3oX2C83u+G6L79sq4w= +github.com/XSAM/otelsql v0.37.0 h1:ya5RNw028JW0eJW8Ma4AmoKxAYsJSGuNVbC7F1J457A= +github.com/XSAM/otelsql v0.37.0/go.mod h1:LHbCu49iU8p255nCn1oi04oX2UjSoRcUMiKEHo2a5qM= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8= +github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI= +github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= +github.com/alecthomas/concurrency v0.0.2/go.mod h1:GmuQb/iHX7mbNtPlC/WDzEFxDMB0HYFer2Qda9QTs7w= +github.com/alecthomas/kong v1.8.1 h1:6aamvWBE/REnR/BCq10EcozmcpUPc5aGI1lPAWdB0EE= +github.com/alecthomas/kong v1.8.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= +github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= +github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/types v0.18.0 h1:47chAUBQdPJVQxElDLT/M1+sAodM0sauRscDPrhPbnY= +github.com/alecthomas/types v0.18.0/go.mod h1:zGEr/iJAi+RoG7LaH0YfWBFcpHDROBkOdmmKcK+BiNg= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.8 h1:RpwAfYcV2lr/yRc4lWhUM9JRPQqKgKWmou3LV7UfWP4= +github.com/aws/aws-sdk-go-v2/config v1.29.8/go.mod h1:t+G7Fq1OcO8cXTPPXzxQSnj/5Xzdc9jAAD3Xrn9/Mgo= +github.com/aws/aws-sdk-go-v2/credentials v1.17.61 h1:Hd/uX6Wo2iUW1JWII+rmyCD7MMhOe7ALwQXN6sKDd1o= +github.com/aws/aws-sdk-go-v2/credentials v1.17.61/go.mod h1:L7vaLkwHY1qgW0gG1zG0z/X0sQ5tpIY5iI13+j3qI80= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.11 h1:qDk85oQdhwP4NR1RpkN+t40aN46/K96hF9J1vDRrkKM= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.11/go.mod h1:f3MkXuZsT+wY24nLIP+gFUuIVQkpVopxbpUD/GUZK0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 h1:2U9sF8nKy7UgyEeLiZTRg6ShBS22z8UnYpV6aRFL0is= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.0/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 h1:wjAdc85cXdQR5uLx5FwWvGIHm4OPJhTyzUHU8craXtE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.16 h1:BHEK2Q/7CMRMCb3nySi/w8UbIcPhKvYP5s1xf8/izn0= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.16/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= +github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/block/ftl v0.460.0 h1:4YNNEtH6x7snXQkbhEgLU0aD/F5od4bUEd40Onf1IQo= +github.com/block/ftl v0.460.0/go.mod h1:OglQ7QbLq8OSsTGeV89Ei13GzNRKqEzwrqzlvv92qOw= +github.com/block/ftl-mysql-auth-proxy v0.0.0-20250226012434-b90030aa165f h1:sxtpCkw9g8jWTdex+r80NspsWwxSU2JuB+L647fnLlY= +github.com/block/ftl-mysql-auth-proxy v0.0.0-20250226012434-b90030aa165f/go.mod h1:h5p2p8WGPrwRk3CzvbbIN+uL0LJomz6mZwE97f6UaZw= +github.com/block/scaffolder v1.3.0 h1:6oMegz48abf6CehW0NF9V0irKPTZoJNsSUTFslvuftw= +github.com/block/scaffolder v1.3.0/go.mod h1:qEzIKpqg42/Xz5vrK5UPtDiYV+M2E5s9Cs/ekoNUFD4= +github.com/bool64/dev v0.2.38 h1:C5H9wkx/BhTYRfV14X90iIQKpSuhzsG+OHQvWdQ5YQ4= +github.com/bool64/dev v0.2.38/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= +github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= +github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= +github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= +github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= +github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= +github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= +github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= +github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= +github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= +github.com/swaggest/jsonschema-go v0.3.73 h1:gU1pBzF3pkZ1GDD3dRMdQoCjrA0sldJ+QcM7aSSPgvc= +github.com/swaggest/jsonschema-go v0.3.73/go.mod h1:qp+Ym2DIXHlHzch3HKz50gPf2wJhKOrAB/VYqLS2oJU= +github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I= +github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= +github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 h1:ajl4QczuJVA2TU9W9AGw++86Xga/RKt//16z/yxPgdk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0/go.mod h1:Vn3/rlOJ3ntf/Q3zAI0V5lDnTbHGaUsNUeF6nZmm7pA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2 h1:35ZFtrCgaAjF7AFAK0+lRSf+4AyYnWRbH7og13p7rZ4= +google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:W9ynFDP/shebLB1Hl/ESTOap2jHd6pmLXPNZC7SVDbA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 h1:DMTIbak9GhdaSxEjvVzAeNZvyc03I61duqNbnm3SU0M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +istio.io/api v1.24.3 h1:iwWWPM0uEQ+oxRHvIWoB8MQ4bjF3dRQj+M5IDVczg0M= +istio.io/api v1.24.3/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I= +istio.io/client-go v1.24.3 h1:TB8IcM3yyMCDzKRJo0YfFOUGNQmkhwH/JE/Yr3lzVAk= +istio.io/client-go v1.24.3/go.mod h1:zSyw/c4luKQKosFIHQaWAQOA0c3bODu4SahQCAMlKA4= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= +modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8= +modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/buildengine/testdata/go/child/types.ftl.go b/internal/buildengine/testdata/go/child/types.ftl.go new file mode 100644 index 0000000000..6f9d761334 --- /dev/null +++ b/internal/buildengine/testdata/go/child/types.ftl.go @@ -0,0 +1,21 @@ +// Code generated by FTL. DO NOT EDIT. +package child + +import ( + "context" + ftlparent "ftl/parent" + "github.com/block/ftl/common/reflection" + "github.com/block/ftl/go-runtime/server" +) + +type HelloClient func(context.Context, ftlparent.HelloRequest) (ftlparent.HelloResponse, error) + +func init() { + reflection.Register( + + reflection.ProvideResourcesForVerb( + Hello, + server.VerbClient[ftlparent.Verb1Client, ftlparent.HelloRequest, ftlparent.HelloResponse](), + ), + ) +} diff --git a/internal/buildengine/testdata/go/parent/ftl.toml b/internal/buildengine/testdata/go/parent/ftl.toml new file mode 100644 index 0000000000..69399f2d89 --- /dev/null +++ b/internal/buildengine/testdata/go/parent/ftl.toml @@ -0,0 +1,2 @@ +module = "parent" +language = "go" diff --git a/internal/buildengine/testdata/go/parent/go.mod b/internal/buildengine/testdata/go/parent/go.mod new file mode 100644 index 0000000000..0638a26004 --- /dev/null +++ b/internal/buildengine/testdata/go/parent/go.mod @@ -0,0 +1,50 @@ +module ftl/parent + +go 1.24.0 + +require github.com/block/ftl v0.460.0 + +require ( + al.essio.dev/pkg/shellescape v1.5.1 // indirect + connectrpc.com/connect v1.17.0 // indirect + connectrpc.com/grpcreflect v1.3.0 // indirect + connectrpc.com/otelconnect v0.7.2 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/alecthomas/atomic v0.1.0-alpha2 // indirect + github.com/alecthomas/concurrency v0.0.2 // indirect + github.com/alecthomas/participle/v2 v2.1.1 // indirect + github.com/alecthomas/types v0.18.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/danieljoos/wincred v1.2.2 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.9.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect + github.com/swaggest/jsonschema-go v0.3.73 // indirect + github.com/swaggest/refl v1.3.0 // indirect + github.com/zalando/go-keyring v0.2.6 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect +) diff --git a/internal/buildengine/testdata/go/parent/go.sum b/internal/buildengine/testdata/go/parent/go.sum new file mode 100644 index 0000000000..abf4b11cb8 --- /dev/null +++ b/internal/buildengine/testdata/go/parent/go.sum @@ -0,0 +1,292 @@ +al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= +al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= +connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= +connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= +connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= +connectrpc.com/otelconnect v0.7.2 h1:WlnwFzaW64dN06JXU+hREPUGeEzpz3Acz2ACOmN8cMI= +connectrpc.com/otelconnect v0.7.2/go.mod h1:JS7XUKfuJs2adhCnXhNHPHLz6oAaZniCJdSF00OZSew= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/IBM/sarama v1.45.1 h1:nY30XqYpqyXOXSNoe2XCgjj9jklGM1Ye94ierUb1jQ0= +github.com/IBM/sarama v1.45.1/go.mod h1:qifDhA3VWSrQ1TjSMyxDl3nYL3oX2C83u+G6L79sq4w= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8= +github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI= +github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= +github.com/alecthomas/concurrency v0.0.2/go.mod h1:GmuQb/iHX7mbNtPlC/WDzEFxDMB0HYFer2Qda9QTs7w= +github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= +github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/types v0.18.0 h1:47chAUBQdPJVQxElDLT/M1+sAodM0sauRscDPrhPbnY= +github.com/alecthomas/types v0.18.0/go.mod h1:zGEr/iJAi+RoG7LaH0YfWBFcpHDROBkOdmmKcK+BiNg= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.8 h1:RpwAfYcV2lr/yRc4lWhUM9JRPQqKgKWmou3LV7UfWP4= +github.com/aws/aws-sdk-go-v2/config v1.29.8/go.mod h1:t+G7Fq1OcO8cXTPPXzxQSnj/5Xzdc9jAAD3Xrn9/Mgo= +github.com/aws/aws-sdk-go-v2/credentials v1.17.61 h1:Hd/uX6Wo2iUW1JWII+rmyCD7MMhOe7ALwQXN6sKDd1o= +github.com/aws/aws-sdk-go-v2/credentials v1.17.61/go.mod h1:L7vaLkwHY1qgW0gG1zG0z/X0sQ5tpIY5iI13+j3qI80= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.11 h1:qDk85oQdhwP4NR1RpkN+t40aN46/K96hF9J1vDRrkKM= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.5.11/go.mod h1:f3MkXuZsT+wY24nLIP+gFUuIVQkpVopxbpUD/GUZK0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.0 h1:2U9sF8nKy7UgyEeLiZTRg6ShBS22z8UnYpV6aRFL0is= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.0/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0 h1:wjAdc85cXdQR5uLx5FwWvGIHm4OPJhTyzUHU8craXtE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.16 h1:BHEK2Q/7CMRMCb3nySi/w8UbIcPhKvYP5s1xf8/izn0= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.16/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= +github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/block/ftl v0.460.0 h1:4YNNEtH6x7snXQkbhEgLU0aD/F5od4bUEd40Onf1IQo= +github.com/block/ftl v0.460.0/go.mod h1:OglQ7QbLq8OSsTGeV89Ei13GzNRKqEzwrqzlvv92qOw= +github.com/block/ftl-mysql-auth-proxy v0.0.0-20250226012434-b90030aa165f h1:sxtpCkw9g8jWTdex+r80NspsWwxSU2JuB+L647fnLlY= +github.com/block/ftl-mysql-auth-proxy v0.0.0-20250226012434-b90030aa165f/go.mod h1:h5p2p8WGPrwRk3CzvbbIN+uL0LJomz6mZwE97f6UaZw= +github.com/block/scaffolder v1.3.0 h1:6oMegz48abf6CehW0NF9V0irKPTZoJNsSUTFslvuftw= +github.com/block/scaffolder v1.3.0/go.mod h1:qEzIKpqg42/Xz5vrK5UPtDiYV+M2E5s9Cs/ekoNUFD4= +github.com/bool64/dev v0.2.38 h1:C5H9wkx/BhTYRfV14X90iIQKpSuhzsG+OHQvWdQ5YQ4= +github.com/bool64/dev v0.2.38/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= +github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= +github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= +github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= +github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= +github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= +github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= +github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= +github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= +github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= +github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= +github.com/swaggest/jsonschema-go v0.3.73 h1:gU1pBzF3pkZ1GDD3dRMdQoCjrA0sldJ+QcM7aSSPgvc= +github.com/swaggest/jsonschema-go v0.3.73/go.mod h1:qp+Ym2DIXHlHzch3HKz50gPf2wJhKOrAB/VYqLS2oJU= +github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I= +github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= +github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2 h1:35ZFtrCgaAjF7AFAK0+lRSf+4AyYnWRbH7og13p7rZ4= +google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:W9ynFDP/shebLB1Hl/ESTOap2jHd6pmLXPNZC7SVDbA= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +istio.io/api v1.24.3 h1:iwWWPM0uEQ+oxRHvIWoB8MQ4bjF3dRQj+M5IDVczg0M= +istio.io/api v1.24.3/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I= +istio.io/client-go v1.24.3 h1:TB8IcM3yyMCDzKRJo0YfFOUGNQmkhwH/JE/Yr3lzVAk= +istio.io/client-go v1.24.3/go.mod h1:zSyw/c4luKQKosFIHQaWAQOA0c3bODu4SahQCAMlKA4= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= +modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8= +modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/buildengine/testdata/go/parent/parent.go b/internal/buildengine/testdata/go/parent/parent.go new file mode 100644 index 0000000000..c21561330e --- /dev/null +++ b/internal/buildengine/testdata/go/parent/parent.go @@ -0,0 +1,21 @@ +package parent + +import ( + "context" + "fmt" + + "github.com/block/ftl/go-runtime/ftl" // Import the FTL SDK. +) + +type HelloRequest struct { + Name ftl.Option[string] `json:"name"` +} + +type HelloResponse struct { + Message string `json:"message"` +} + +//ftl:verb export +func Verb1(ctx context.Context, req HelloRequest) (HelloResponse, error) { + return HelloResponse{Message: fmt.Sprintf("Hello, %s!", req.Name.Default("anonymous"))}, nil +} diff --git a/internal/buildengine/testdata/go/parent/types.ftl.go b/internal/buildengine/testdata/go/parent/types.ftl.go new file mode 100644 index 0000000000..0056e70afb --- /dev/null +++ b/internal/buildengine/testdata/go/parent/types.ftl.go @@ -0,0 +1,18 @@ +// Code generated by FTL. DO NOT EDIT. +package parent + +import ( + "context" + "github.com/block/ftl/common/reflection" +) + +type Verb1Client func(context.Context, HelloRequest) (HelloResponse, error) + +func init() { + reflection.Register( + + reflection.ProvideResourcesForVerb( + Verb1, + ), + ) +} diff --git a/internal/buildengine/testdata/kotlin/child/ftl.toml b/internal/buildengine/testdata/kotlin/child/ftl.toml new file mode 100644 index 0000000000..7369318566 --- /dev/null +++ b/internal/buildengine/testdata/kotlin/child/ftl.toml @@ -0,0 +1,2 @@ +module = "child" +language = "kotlin" diff --git a/internal/buildengine/testdata/kotlin/child/pom.xml b/internal/buildengine/testdata/kotlin/child/pom.xml new file mode 100644 index 0000000000..06dc613e11 --- /dev/null +++ b/internal/buildengine/testdata/kotlin/child/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + ftl.child + child + 1.0-SNAPSHOT + + + xyz.block.ftl + ftl-build-parent-kotlin + 1.0-SNAPSHOT + + + diff --git a/internal/buildengine/testdata/kotlin/child/src/main/kotlin/ftl/child/Child.kt b/internal/buildengine/testdata/kotlin/child/src/main/kotlin/ftl/child/Child.kt new file mode 100644 index 0000000000..0cca817a4e --- /dev/null +++ b/internal/buildengine/testdata/kotlin/child/src/main/kotlin/ftl/child/Child.kt @@ -0,0 +1,12 @@ +package ftl.child + +import xyz.block.ftl.* +import ftl.parent.* + +@Export +@Verb +fun hello(req: HelloRequest, client: Verb1Client): HelloResponse { + val parentReq = ftl.parent.HelloRequest(req.name) + val parentResp = client.verb1(parentReq) + return HelloResponse(parentResp.message) +} diff --git a/internal/buildengine/testdata/kotlin/child/src/main/resources/application.properties b/internal/buildengine/testdata/kotlin/child/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/internal/buildengine/testdata/kotlin/parent/ftl.toml b/internal/buildengine/testdata/kotlin/parent/ftl.toml new file mode 100644 index 0000000000..8a894d5253 --- /dev/null +++ b/internal/buildengine/testdata/kotlin/parent/ftl.toml @@ -0,0 +1,2 @@ +module = "parent" +language = "kotlin" diff --git a/internal/buildengine/testdata/kotlin/parent/pom.xml b/internal/buildengine/testdata/kotlin/parent/pom.xml new file mode 100644 index 0000000000..dcdc3bd0db --- /dev/null +++ b/internal/buildengine/testdata/kotlin/parent/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + ftl.parent + parent + 1.0-SNAPSHOT + + + xyz.block.ftl + ftl-build-parent-kotlin + 1.0-SNAPSHOT + + + diff --git a/internal/buildengine/testdata/kotlin/parent/src/main/kotlin/ftl/parent/Parent.kt b/internal/buildengine/testdata/kotlin/parent/src/main/kotlin/ftl/parent/Parent.kt new file mode 100644 index 0000000000..a40b617593 --- /dev/null +++ b/internal/buildengine/testdata/kotlin/parent/src/main/kotlin/ftl/parent/Parent.kt @@ -0,0 +1,10 @@ +package ftl.parent + +import xyz.block.ftl.* + +data class HelloRequest(val name: String) +data class HelloResponse(val message: String) + +@Export +@Verb +fun verb1(req: HelloRequest): HelloResponse = HelloResponse("Hello, ${req.name}!") diff --git a/internal/buildengine/testdata/kotlin/parent/src/main/resources/application.properties b/internal/buildengine/testdata/kotlin/parent/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/internal/integration/actions.go b/internal/integration/actions.go index b6ae218b72..5a31cd6b40 100644 --- a/internal/integration/actions.go +++ b/internal/integration/actions.go @@ -312,6 +312,18 @@ func WaitWithTimeout(module string, timeout time.Duration) Action { } } +func WaitForDev(noErrors bool, msgAndArgs ...any) Action { + return func(t testing.TB, ic TestContext) { + ExecWithOutput("ftl", []string{"await-summary"}, func(output string) { + if noErrors { + assert.NotContains(t, output, "[Error]", msgAndArgs...) + } else { + assert.Contains(t, output, "[Error]", msgAndArgs...) + } + })(t, ic) + } +} + func Sleep(duration time.Duration) Action { return func(t testing.TB, ic TestContext) { Infof("Sleeping for %s", duration) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/v1/admin_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/v1/admin_pb2.py index 5311025f5e..455e6f9907 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/v1/admin_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/v1/admin_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import schemaservice_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_schemaservice__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cxyz/block/ftl/v1/admin.proto\x12\x10xyz.block.ftl.v1\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\x1a$xyz/block/ftl/v1/schemaservice.proto\"G\n\tConfigRef\x12\x1b\n\x06module\x18\x01 \x01(\tH\x00R\x06module\x88\x01\x01\x12\x12\n\x04name\x18\x02 \x01(\tR\x04nameB\t\n\x07_module\"\xca\x01\n\x11\x43onfigListRequest\x12\x1b\n\x06module\x18\x01 \x01(\tH\x00R\x06module\x88\x01\x01\x12*\n\x0einclude_values\x18\x02 \x01(\x08H\x01R\rincludeValues\x88\x01\x01\x12\x41\n\x08provider\x18\x03 \x01(\x0e\x32 .xyz.block.ftl.v1.ConfigProviderH\x02R\x08provider\x88\x01\x01\x42\t\n\x07_moduleB\x11\n\x0f_include_valuesB\x0b\n\t_provider\"\xa5\x01\n\x12\x43onfigListResponse\x12\x45\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32+.xyz.block.ftl.v1.ConfigListResponse.ConfigR\x07\x63onfigs\x1aH\n\x06\x43onfig\x12\x19\n\x08ref_path\x18\x01 \x01(\tR\x07refPath\x12\x19\n\x05value\x18\x02 \x01(\x0cH\x00R\x05value\x88\x01\x01\x42\x08\n\x06_value\"A\n\x10\x43onfigGetRequest\x12-\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\")\n\x11\x43onfigGetResponse\x12\x14\n\x05value\x18\x01 \x01(\x0cR\x05value\"\xa7\x01\n\x10\x43onfigSetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.ConfigProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\x12\x14\n\x05value\x18\x03 \x01(\x0cR\x05valueB\x0b\n\t_provider\"\x13\n\x11\x43onfigSetResponse\"\x93\x01\n\x12\x43onfigUnsetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.ConfigProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03refB\x0b\n\t_provider\"\x15\n\x13\x43onfigUnsetResponse\"\xcb\x01\n\x12SecretsListRequest\x12\x1b\n\x06module\x18\x01 \x01(\tH\x00R\x06module\x88\x01\x01\x12*\n\x0einclude_values\x18\x02 \x01(\x08H\x01R\rincludeValues\x88\x01\x01\x12\x41\n\x08provider\x18\x03 \x01(\x0e\x32 .xyz.block.ftl.v1.SecretProviderH\x02R\x08provider\x88\x01\x01\x42\t\n\x07_moduleB\x11\n\x0f_include_valuesB\x0b\n\t_provider\"\xa7\x01\n\x13SecretsListResponse\x12\x46\n\x07secrets\x18\x01 \x03(\x0b\x32,.xyz.block.ftl.v1.SecretsListResponse.SecretR\x07secrets\x1aH\n\x06Secret\x12\x19\n\x08ref_path\x18\x01 \x01(\tR\x07refPath\x12\x19\n\x05value\x18\x02 \x01(\x0cH\x00R\x05value\x88\x01\x01\x42\x08\n\x06_value\"A\n\x10SecretGetRequest\x12-\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\")\n\x11SecretGetResponse\x12\x14\n\x05value\x18\x01 \x01(\x0cR\x05value\"\xa7\x01\n\x10SecretSetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.SecretProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\x12\x14\n\x05value\x18\x03 \x01(\x0cR\x05valueB\x0b\n\t_provider\"\x13\n\x11SecretSetResponse\"\x93\x01\n\x12SecretUnsetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.SecretProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03refB\x0b\n\t_provider\"\x15\n\x13SecretUnsetResponse\"4\n\x1aMapConfigsForModuleRequest\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\"\xab\x01\n\x1bMapConfigsForModuleResponse\x12Q\n\x06values\x18\x01 \x03(\x0b\x32\x39.xyz.block.ftl.v1.MapConfigsForModuleResponse.ValuesEntryR\x06values\x1a\x39\n\x0bValuesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value:\x02\x38\x01\"4\n\x1aMapSecretsForModuleRequest\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\"\xab\x01\n\x1bMapSecretsForModuleResponse\x12Q\n\x06values\x18\x01 \x03(\x0b\x32\x39.xyz.block.ftl.v1.MapSecretsForModuleResponse.ValuesEntryR\x06values\x1a\x39\n\x0bValuesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value:\x02\x38\x01\"\x9a\x01\n\x18ResetSubscriptionRequest\x12@\n\x0csubscription\x18\x01 \x01(\x0b\x32\x1c.xyz.block.ftl.schema.v1.RefR\x0csubscription\x12<\n\x06offset\x18\x02 \x01(\x0e\x32$.xyz.block.ftl.v1.SubscriptionOffsetR\x06offset\"\x1b\n\x19ResetSubscriptionResponse\"o\n\x15\x41pplyChangesetRequest\x12\x39\n\x07modules\x18\x01 \x03(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x07modules\x12\x1b\n\tto_remove\x18\x02 \x03(\tR\x08toRemove\"Z\n\x16\x41pplyChangesetResponse\x12@\n\tchangeset\x18\x02 \x01(\x0b\x32\".xyz.block.ftl.schema.v1.ChangesetR\tchangeset\"@\n\x17GetArtefactDiffsRequest\x12%\n\x0e\x63lient_digests\x18\x01 \x03(\tR\rclientDigests\"\x94\x01\n\x18GetArtefactDiffsResponse\x12\'\n\x0fmissing_digests\x18\x01 \x03(\tR\x0emissingDigests\x12O\n\x10\x63lient_artefacts\x18\x02 \x03(\x0b\x32$.xyz.block.ftl.v1.DeploymentArtefactR\x0f\x63lientArtefacts\"\x93\x01\n\x1dGetDeploymentArtefactsRequest\x12%\n\x0e\x64\x65ployment_key\x18\x01 \x01(\tR\rdeploymentKey\x12K\n\x0ehave_artefacts\x18\x02 \x03(\x0b\x32$.xyz.block.ftl.v1.DeploymentArtefactR\rhaveArtefacts\"x\n\x1eGetDeploymentArtefactsResponse\x12@\n\x08\x61rtefact\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.v1.DeploymentArtefactR\x08\x61rtefact\x12\x14\n\x05\x63hunk\x18\x02 \x01(\x0cR\x05\x63hunk\"`\n\x12\x44\x65ploymentArtefact\x12\x16\n\x06\x64igest\x18\x01 \x01(\x0cR\x06\x64igest\x12\x12\n\x04path\x18\x02 \x01(\tR\x04path\x12\x1e\n\nexecutable\x18\x03 \x01(\x08R\nexecutable\"Y\n\x15UploadArtefactRequest\x12\x16\n\x06\x64igest\x18\x01 \x01(\x0cR\x06\x64igest\x12\x12\n\x04size\x18\x02 \x01(\x03R\x04size\x12\x14\n\x05\x63hunk\x18\x03 \x01(\x0cR\x05\x63hunk\"\x18\n\x16UploadArtefactResponse\"\x14\n\x12\x43lusterInfoRequest\"9\n\x13\x43lusterInfoResponse\x12\x0e\n\x02os\x18\x01 \x01(\tR\x02os\x12\x12\n\x04\x61rch\x18\x02 \x01(\tR\x04\x61rch*h\n\x0e\x43onfigProvider\x12\x1f\n\x1b\x43ONFIG_PROVIDER_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43ONFIG_PROVIDER_INLINE\x10\x01\x12\x19\n\x15\x43ONFIG_PROVIDER_ENVAR\x10\x02*\xb7\x01\n\x0eSecretProvider\x12\x1f\n\x1bSECRET_PROVIDER_UNSPECIFIED\x10\x00\x12\x1a\n\x16SECRET_PROVIDER_INLINE\x10\x01\x12\x19\n\x15SECRET_PROVIDER_ENVAR\x10\x02\x12\x1c\n\x18SECRET_PROVIDER_KEYCHAIN\x10\x03\x12\x16\n\x12SECRET_PROVIDER_OP\x10\x04\x12\x17\n\x13SECRET_PROVIDER_ASM\x10\x05*{\n\x12SubscriptionOffset\x12#\n\x1fSUBSCRIPTION_OFFSET_UNSPECIFIED\x10\x00\x12 \n\x1cSUBSCRIPTION_OFFSET_EARLIEST\x10\x01\x12\x1e\n\x1aSUBSCRIPTION_OFFSET_LATEST\x10\x02\x32\x92\x10\n\x0c\x41\x64minService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12W\n\nConfigList\x12#.xyz.block.ftl.v1.ConfigListRequest\x1a$.xyz.block.ftl.v1.ConfigListResponse\x12T\n\tConfigGet\x12\".xyz.block.ftl.v1.ConfigGetRequest\x1a#.xyz.block.ftl.v1.ConfigGetResponse\x12T\n\tConfigSet\x12\".xyz.block.ftl.v1.ConfigSetRequest\x1a#.xyz.block.ftl.v1.ConfigSetResponse\x12Z\n\x0b\x43onfigUnset\x12$.xyz.block.ftl.v1.ConfigUnsetRequest\x1a%.xyz.block.ftl.v1.ConfigUnsetResponse\x12Z\n\x0bSecretsList\x12$.xyz.block.ftl.v1.SecretsListRequest\x1a%.xyz.block.ftl.v1.SecretsListResponse\x12T\n\tSecretGet\x12\".xyz.block.ftl.v1.SecretGetRequest\x1a#.xyz.block.ftl.v1.SecretGetResponse\x12T\n\tSecretSet\x12\".xyz.block.ftl.v1.SecretSetRequest\x1a#.xyz.block.ftl.v1.SecretSetResponse\x12Z\n\x0bSecretUnset\x12$.xyz.block.ftl.v1.SecretUnsetRequest\x1a%.xyz.block.ftl.v1.SecretUnsetResponse\x12r\n\x13MapConfigsForModule\x12,.xyz.block.ftl.v1.MapConfigsForModuleRequest\x1a-.xyz.block.ftl.v1.MapConfigsForModuleResponse\x12r\n\x13MapSecretsForModule\x12,.xyz.block.ftl.v1.MapSecretsForModuleRequest\x1a-.xyz.block.ftl.v1.MapSecretsForModuleResponse\x12l\n\x11ResetSubscription\x12*.xyz.block.ftl.v1.ResetSubscriptionRequest\x1a+.xyz.block.ftl.v1.ResetSubscriptionResponse\x12\x63\n\x0e\x41pplyChangeset\x12\'.xyz.block.ftl.v1.ApplyChangesetRequest\x1a(.xyz.block.ftl.v1.ApplyChangesetResponse\x12Y\n\tGetSchema\x12\".xyz.block.ftl.v1.GetSchemaRequest\x1a#.xyz.block.ftl.v1.GetSchemaResponse\"\x03\x90\x02\x01\x12^\n\nPullSchema\x12#.xyz.block.ftl.v1.PullSchemaRequest\x1a$.xyz.block.ftl.v1.PullSchemaResponse\"\x03\x90\x02\x01\x30\x01\x12l\n\x11RollbackChangeset\x12*.xyz.block.ftl.v1.RollbackChangesetRequest\x1a+.xyz.block.ftl.v1.RollbackChangesetResponse\x12`\n\rFailChangeset\x12&.xyz.block.ftl.v1.FailChangesetRequest\x1a\'.xyz.block.ftl.v1.FailChangesetResponse\x12Z\n\x0b\x43lusterInfo\x12$.xyz.block.ftl.v1.ClusterInfoRequest\x1a%.xyz.block.ftl.v1.ClusterInfoResponse\x12i\n\x10GetArtefactDiffs\x12).xyz.block.ftl.v1.GetArtefactDiffsRequest\x1a*.xyz.block.ftl.v1.GetArtefactDiffsResponse\x12}\n\x16GetDeploymentArtefacts\x12/.xyz.block.ftl.v1.GetDeploymentArtefactsRequest\x1a\x30.xyz.block.ftl.v1.GetDeploymentArtefactsResponse0\x01\x12\x65\n\x0eUploadArtefact\x12\'.xyz.block.ftl.v1.UploadArtefactRequest\x1a(.xyz.block.ftl.v1.UploadArtefactResponse(\x01\x42>P\x01Z:github.com/block/ftl/backend/protos/xyz/block/ftl/v1;ftlv1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cxyz/block/ftl/v1/admin.proto\x12\x10xyz.block.ftl.v1\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\x1a$xyz/block/ftl/v1/schemaservice.proto\"G\n\tConfigRef\x12\x1b\n\x06module\x18\x01 \x01(\tH\x00R\x06module\x88\x01\x01\x12\x12\n\x04name\x18\x02 \x01(\tR\x04nameB\t\n\x07_module\"\xca\x01\n\x11\x43onfigListRequest\x12\x1b\n\x06module\x18\x01 \x01(\tH\x00R\x06module\x88\x01\x01\x12*\n\x0einclude_values\x18\x02 \x01(\x08H\x01R\rincludeValues\x88\x01\x01\x12\x41\n\x08provider\x18\x03 \x01(\x0e\x32 .xyz.block.ftl.v1.ConfigProviderH\x02R\x08provider\x88\x01\x01\x42\t\n\x07_moduleB\x11\n\x0f_include_valuesB\x0b\n\t_provider\"\xa5\x01\n\x12\x43onfigListResponse\x12\x45\n\x07\x63onfigs\x18\x01 \x03(\x0b\x32+.xyz.block.ftl.v1.ConfigListResponse.ConfigR\x07\x63onfigs\x1aH\n\x06\x43onfig\x12\x19\n\x08ref_path\x18\x01 \x01(\tR\x07refPath\x12\x19\n\x05value\x18\x02 \x01(\x0cH\x00R\x05value\x88\x01\x01\x42\x08\n\x06_value\"A\n\x10\x43onfigGetRequest\x12-\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\")\n\x11\x43onfigGetResponse\x12\x14\n\x05value\x18\x01 \x01(\x0cR\x05value\"\xa7\x01\n\x10\x43onfigSetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.ConfigProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\x12\x14\n\x05value\x18\x03 \x01(\x0cR\x05valueB\x0b\n\t_provider\"\x13\n\x11\x43onfigSetResponse\"\x93\x01\n\x12\x43onfigUnsetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.ConfigProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03refB\x0b\n\t_provider\"\x15\n\x13\x43onfigUnsetResponse\"\xcb\x01\n\x12SecretsListRequest\x12\x1b\n\x06module\x18\x01 \x01(\tH\x00R\x06module\x88\x01\x01\x12*\n\x0einclude_values\x18\x02 \x01(\x08H\x01R\rincludeValues\x88\x01\x01\x12\x41\n\x08provider\x18\x03 \x01(\x0e\x32 .xyz.block.ftl.v1.SecretProviderH\x02R\x08provider\x88\x01\x01\x42\t\n\x07_moduleB\x11\n\x0f_include_valuesB\x0b\n\t_provider\"\xa7\x01\n\x13SecretsListResponse\x12\x46\n\x07secrets\x18\x01 \x03(\x0b\x32,.xyz.block.ftl.v1.SecretsListResponse.SecretR\x07secrets\x1aH\n\x06Secret\x12\x19\n\x08ref_path\x18\x01 \x01(\tR\x07refPath\x12\x19\n\x05value\x18\x02 \x01(\x0cH\x00R\x05value\x88\x01\x01\x42\x08\n\x06_value\"A\n\x10SecretGetRequest\x12-\n\x03ref\x18\x01 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\")\n\x11SecretGetResponse\x12\x14\n\x05value\x18\x01 \x01(\x0cR\x05value\"\xa7\x01\n\x10SecretSetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.SecretProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03ref\x12\x14\n\x05value\x18\x03 \x01(\x0cR\x05valueB\x0b\n\t_provider\"\x13\n\x11SecretSetResponse\"\x93\x01\n\x12SecretUnsetRequest\x12\x41\n\x08provider\x18\x01 \x01(\x0e\x32 .xyz.block.ftl.v1.SecretProviderH\x00R\x08provider\x88\x01\x01\x12-\n\x03ref\x18\x02 \x01(\x0b\x32\x1b.xyz.block.ftl.v1.ConfigRefR\x03refB\x0b\n\t_provider\"\x15\n\x13SecretUnsetResponse\"4\n\x1aMapConfigsForModuleRequest\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\"\xab\x01\n\x1bMapConfigsForModuleResponse\x12Q\n\x06values\x18\x01 \x03(\x0b\x32\x39.xyz.block.ftl.v1.MapConfigsForModuleResponse.ValuesEntryR\x06values\x1a\x39\n\x0bValuesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value:\x02\x38\x01\"4\n\x1aMapSecretsForModuleRequest\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\"\xab\x01\n\x1bMapSecretsForModuleResponse\x12Q\n\x06values\x18\x01 \x03(\x0b\x32\x39.xyz.block.ftl.v1.MapSecretsForModuleResponse.ValuesEntryR\x06values\x1a\x39\n\x0bValuesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value:\x02\x38\x01\"\x9a\x01\n\x18ResetSubscriptionRequest\x12@\n\x0csubscription\x18\x01 \x01(\x0b\x32\x1c.xyz.block.ftl.schema.v1.RefR\x0csubscription\x12<\n\x06offset\x18\x02 \x01(\x0e\x32$.xyz.block.ftl.v1.SubscriptionOffsetR\x06offset\"\x1b\n\x19ResetSubscriptionResponse\"o\n\x15\x41pplyChangesetRequest\x12\x39\n\x07modules\x18\x01 \x03(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x07modules\x12\x1b\n\tto_remove\x18\x02 \x03(\tR\x08toRemove\"Z\n\x16\x41pplyChangesetResponse\x12@\n\tchangeset\x18\x02 \x01(\x0b\x32\".xyz.block.ftl.schema.v1.ChangesetR\tchangeset\"@\n\x17GetArtefactDiffsRequest\x12%\n\x0e\x63lient_digests\x18\x01 \x03(\tR\rclientDigests\"\x94\x01\n\x18GetArtefactDiffsResponse\x12\'\n\x0fmissing_digests\x18\x01 \x03(\tR\x0emissingDigests\x12O\n\x10\x63lient_artefacts\x18\x02 \x03(\x0b\x32$.xyz.block.ftl.v1.DeploymentArtefactR\x0f\x63lientArtefacts\"\x93\x01\n\x1dGetDeploymentArtefactsRequest\x12%\n\x0e\x64\x65ployment_key\x18\x01 \x01(\tR\rdeploymentKey\x12K\n\x0ehave_artefacts\x18\x02 \x03(\x0b\x32$.xyz.block.ftl.v1.DeploymentArtefactR\rhaveArtefacts\"x\n\x1eGetDeploymentArtefactsResponse\x12@\n\x08\x61rtefact\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.v1.DeploymentArtefactR\x08\x61rtefact\x12\x14\n\x05\x63hunk\x18\x02 \x01(\x0cR\x05\x63hunk\"`\n\x12\x44\x65ploymentArtefact\x12\x16\n\x06\x64igest\x18\x01 \x01(\x0cR\x06\x64igest\x12\x12\n\x04path\x18\x02 \x01(\tR\x04path\x12\x1e\n\nexecutable\x18\x03 \x01(\x08R\nexecutable\"Y\n\x15UploadArtefactRequest\x12\x16\n\x06\x64igest\x18\x01 \x01(\x0cR\x06\x64igest\x12\x12\n\x04size\x18\x02 \x01(\x03R\x04size\x12\x14\n\x05\x63hunk\x18\x03 \x01(\x0cR\x05\x63hunk\"\x18\n\x16UploadArtefactResponse\"\x14\n\x12\x43lusterInfoRequest\"9\n\x13\x43lusterInfoResponse\x12\x0e\n\x02os\x18\x01 \x01(\tR\x02os\x12\x12\n\x04\x61rch\x18\x02 \x01(\tR\x04\x61rch*h\n\x0e\x43onfigProvider\x12\x1f\n\x1b\x43ONFIG_PROVIDER_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43ONFIG_PROVIDER_INLINE\x10\x01\x12\x19\n\x15\x43ONFIG_PROVIDER_ENVAR\x10\x02*\xb7\x01\n\x0eSecretProvider\x12\x1f\n\x1bSECRET_PROVIDER_UNSPECIFIED\x10\x00\x12\x1a\n\x16SECRET_PROVIDER_INLINE\x10\x01\x12\x19\n\x15SECRET_PROVIDER_ENVAR\x10\x02\x12\x1c\n\x18SECRET_PROVIDER_KEYCHAIN\x10\x03\x12\x16\n\x12SECRET_PROVIDER_OP\x10\x04\x12\x17\n\x13SECRET_PROVIDER_ASM\x10\x05*{\n\x12SubscriptionOffset\x12#\n\x1fSUBSCRIPTION_OFFSET_UNSPECIFIED\x10\x00\x12 \n\x1cSUBSCRIPTION_OFFSET_EARLIEST\x10\x01\x12\x1e\n\x1aSUBSCRIPTION_OFFSET_LATEST\x10\x02\x32\x94\x10\n\x0c\x41\x64minService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12W\n\nConfigList\x12#.xyz.block.ftl.v1.ConfigListRequest\x1a$.xyz.block.ftl.v1.ConfigListResponse\x12T\n\tConfigGet\x12\".xyz.block.ftl.v1.ConfigGetRequest\x1a#.xyz.block.ftl.v1.ConfigGetResponse\x12T\n\tConfigSet\x12\".xyz.block.ftl.v1.ConfigSetRequest\x1a#.xyz.block.ftl.v1.ConfigSetResponse\x12Z\n\x0b\x43onfigUnset\x12$.xyz.block.ftl.v1.ConfigUnsetRequest\x1a%.xyz.block.ftl.v1.ConfigUnsetResponse\x12Z\n\x0bSecretsList\x12$.xyz.block.ftl.v1.SecretsListRequest\x1a%.xyz.block.ftl.v1.SecretsListResponse\x12T\n\tSecretGet\x12\".xyz.block.ftl.v1.SecretGetRequest\x1a#.xyz.block.ftl.v1.SecretGetResponse\x12T\n\tSecretSet\x12\".xyz.block.ftl.v1.SecretSetRequest\x1a#.xyz.block.ftl.v1.SecretSetResponse\x12Z\n\x0bSecretUnset\x12$.xyz.block.ftl.v1.SecretUnsetRequest\x1a%.xyz.block.ftl.v1.SecretUnsetResponse\x12r\n\x13MapConfigsForModule\x12,.xyz.block.ftl.v1.MapConfigsForModuleRequest\x1a-.xyz.block.ftl.v1.MapConfigsForModuleResponse\x12r\n\x13MapSecretsForModule\x12,.xyz.block.ftl.v1.MapSecretsForModuleRequest\x1a-.xyz.block.ftl.v1.MapSecretsForModuleResponse\x12l\n\x11ResetSubscription\x12*.xyz.block.ftl.v1.ResetSubscriptionRequest\x1a+.xyz.block.ftl.v1.ResetSubscriptionResponse\x12\x65\n\x0e\x41pplyChangeset\x12\'.xyz.block.ftl.v1.ApplyChangesetRequest\x1a(.xyz.block.ftl.v1.ApplyChangesetResponse0\x01\x12Y\n\tGetSchema\x12\".xyz.block.ftl.v1.GetSchemaRequest\x1a#.xyz.block.ftl.v1.GetSchemaResponse\"\x03\x90\x02\x01\x12^\n\nPullSchema\x12#.xyz.block.ftl.v1.PullSchemaRequest\x1a$.xyz.block.ftl.v1.PullSchemaResponse\"\x03\x90\x02\x01\x30\x01\x12l\n\x11RollbackChangeset\x12*.xyz.block.ftl.v1.RollbackChangesetRequest\x1a+.xyz.block.ftl.v1.RollbackChangesetResponse\x12`\n\rFailChangeset\x12&.xyz.block.ftl.v1.FailChangesetRequest\x1a\'.xyz.block.ftl.v1.FailChangesetResponse\x12Z\n\x0b\x43lusterInfo\x12$.xyz.block.ftl.v1.ClusterInfoRequest\x1a%.xyz.block.ftl.v1.ClusterInfoResponse\x12i\n\x10GetArtefactDiffs\x12).xyz.block.ftl.v1.GetArtefactDiffsRequest\x1a*.xyz.block.ftl.v1.GetArtefactDiffsResponse\x12}\n\x16GetDeploymentArtefacts\x12/.xyz.block.ftl.v1.GetDeploymentArtefactsRequest\x1a\x30.xyz.block.ftl.v1.GetDeploymentArtefactsResponse0\x01\x12\x65\n\x0eUploadArtefact\x12\'.xyz.block.ftl.v1.UploadArtefactRequest\x1a(.xyz.block.ftl.v1.UploadArtefactResponse(\x01\x42>P\x01Z:github.com/block/ftl/backend/protos/xyz/block/ftl/v1;ftlv1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -128,5 +128,5 @@ _globals['_CLUSTERINFORESPONSE']._serialized_start=3497 _globals['_CLUSTERINFORESPONSE']._serialized_end=3554 _globals['_ADMINSERVICE']._serialized_start=3974 - _globals['_ADMINSERVICE']._serialized_end=6040 + _globals['_ADMINSERVICE']._serialized_end=6042 # @@protoc_insertion_point(module_scope)