@@ -52,8 +52,11 @@ type AppK8sClientInterface interface {
5252 // Returns a not-found error (checkable with k8serrors.IsNotFound) if the KService does not exist.
5353 GetStatus (ctx context.Context , appID * flyteapp.Identifier ) (* flyteapp.Status , error )
5454
55- // List returns all apps (spec + live status) for the given project/domain scope.
56- List (ctx context.Context , project , domain string ) ([]* flyteapp.App , error )
55+ // List returns apps for the given project/domain scope with optional pagination.
56+ // If appName is non-empty, only the app with that name is returned.
57+ // limit=0 means no limit. token is the K8s continue token from a previous call.
58+ // Returns the apps, the continue token for the next page (empty if last page), and any error.
59+ List (ctx context.Context , project , domain , appName string , limit uint32 , token string ) ([]* flyteapp.App , string , error )
5760
5861 // Delete removes the KService CRD entirely. The app must be re-created from scratch.
5962 // Use Stop to scale to zero while preserving the KService.
@@ -66,8 +69,9 @@ type AppK8sClientInterface interface {
6669 DeleteReplica (ctx context.Context , replicaID * flyteapp.ReplicaIdentifier ) error
6770
6871 // Watch returns a channel of WatchResponse events for KServices matching the
69- // given project/domain scope. The channel is closed when ctx is cancelled.
70- Watch (ctx context.Context , project , domain string ) (<- chan * flyteapp.WatchResponse , error )
72+ // given project/domain scope. If appName is non-empty, only events for that
73+ // specific app are returned. The channel is closed when ctx is cancelled.
74+ Watch (ctx context.Context , project , domain , appName string ) (<- chan * flyteapp.WatchResponse , error )
7175}
7276
7377// AppK8sClient implements AppK8sClientInterface using controller-runtime.
@@ -169,13 +173,20 @@ func (c *AppK8sClient) Delete(ctx context.Context, appID *flyteapp.Identifier) e
169173}
170174
171175// Watch returns a channel of WatchResponse events for KServices in the given
172- // project/domain scope. The channel is closed when ctx is cancelled or the
176+ // project/domain scope. If appName is non-empty, only events for that specific
177+ // app are returned. The channel is closed when ctx is cancelled or the
173178// underlying watch terminates.
174- func (c * AppK8sClient ) Watch (ctx context.Context , project , domain string ) (<- chan * flyteapp.WatchResponse , error ) {
179+ func (c * AppK8sClient ) Watch (ctx context.Context , project , domain , appName string ) (<- chan * flyteapp.WatchResponse , error ) {
175180 ns := appNamespace (project , domain )
181+
182+ labels := map [string ]string {labelAppManaged : "true" }
183+ if appName != "" {
184+ labels [labelAppName ] = strings .ToLower (appName )
185+ }
186+
176187 watcher , err := c .k8sClient .Watch (ctx , & servingv1.ServiceList {},
177188 client .InNamespace (ns ),
178- client.MatchingLabels { labelAppManaged : "true" } ,
189+ client .MatchingLabels ( labels ) ,
179190 )
180191 if err != nil {
181192 return nil , fmt .Errorf ("failed to start KService watch in namespace %s: %w" , ns , err )
@@ -258,16 +269,28 @@ func (c *AppK8sClient) GetStatus(ctx context.Context, appID *flyteapp.Identifier
258269 return c .kserviceToStatus (ctx , ksvc ), nil
259270}
260271
261- // List returns all apps for the given project/domain by listing KServices in the
262- // project/domain namespace.
263- func (c * AppK8sClient ) List (ctx context.Context , project , domain string ) ([]* flyteapp.App , error ) {
272+ // List returns apps for the given project/domain scope with optional pagination.
273+ func (c * AppK8sClient ) List (ctx context.Context , project , domain , appName string , limit uint32 , token string ) ([]* flyteapp.App , string , error ) {
264274 ns := appNamespace (project , domain )
265- list := & servingv1.ServiceList {}
266- if err := c .k8sClient .List (ctx , list ,
275+
276+ matchLabels := client.MatchingLabels {labelAppManaged : "true" }
277+ if appName != "" {
278+ matchLabels [labelAppName ] = strings .ToLower (appName )
279+ }
280+ listOpts := []client.ListOption {
267281 client .InNamespace (ns ),
268- client.MatchingLabels {labelAppManaged : "true" },
269- ); err != nil {
270- return nil , fmt .Errorf ("failed to list KServices for %s/%s: %w" , project , domain , err )
282+ matchLabels ,
283+ }
284+ if limit > 0 {
285+ listOpts = append (listOpts , client .Limit (int64 (limit )))
286+ }
287+ if token != "" {
288+ listOpts = append (listOpts , client .Continue (token ))
289+ }
290+
291+ list := & servingv1.ServiceList {}
292+ if err := c .k8sClient .List (ctx , list , listOpts ... ); err != nil {
293+ return nil , "" , fmt .Errorf ("failed to list KServices for %s/%s: %w" , project , domain , err )
271294 }
272295
273296 apps := make ([]* flyteapp.App , 0 , len (list .Items ))
@@ -279,7 +302,7 @@ func (c *AppK8sClient) List(ctx context.Context, project, domain string) ([]*fly
279302 }
280303 apps = append (apps , a )
281304 }
282- return apps , nil
305+ return apps , list . Continue , nil
283306}
284307
285308// --- Helpers ---
@@ -302,7 +325,7 @@ func kserviceName(id *flyteapp.Identifier) string {
302325
303326// specSHA computes a SHA256 digest of the serialized App Spec proto.
304327func specSHA (spec * flyteapp.Spec ) (string , error ) {
305- b , err := proto.MarshalOptions { Deterministic : true }. Marshal (spec )
328+ b , err := proto .Marshal (spec )
306329 if err != nil {
307330 return "" , fmt .Errorf ("failed to marshal spec: %w" , err )
308331 }
@@ -358,10 +381,6 @@ func (c *AppK8sClient) buildKService(app *flyteapp.App) (*servingv1.Service, err
358381 Template : servingv1.RevisionTemplateSpec {
359382 ObjectMeta : metav1.ObjectMeta {
360383 Annotations : templateAnnotations ,
361- Labels : map [string ]string {
362- labelAppManaged : "true" ,
363- labelAppName : appID .GetName (),
364- },
365384 },
366385 Spec : servingv1.RevisionSpec {
367386 PodSpec : podSpec ,
@@ -467,8 +486,8 @@ func (c *AppK8sClient) kserviceToStatus(ctx context.Context, ksvc *servingv1.Ser
467486 phase = flyteapp .Status_DEPLOYMENT_STATUS_ACTIVE
468487 case ksvc .IsFailed ():
469488 phase = flyteapp .Status_DEPLOYMENT_STATUS_FAILED
470- if condition := ksvc .Status .GetCondition (servingv1 .ServiceConditionReady ); condition != nil {
471- message = condition .Message
489+ if c := ksvc .Status .GetCondition (servingv1 .ServiceConditionReady ); c != nil {
490+ message = c .Message
472491 }
473492 case ksvc .Status .LatestCreatedRevisionName != ksvc .Status .LatestReadyRevisionName :
474493 phase = flyteapp .Status_DEPLOYMENT_STATUS_DEPLOYING
0 commit comments