github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/internal/swarm/states.go (about)

     1  package swarm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/Prakhar-Agarwal-byte/moby/api/types"
     8  	"github.com/Prakhar-Agarwal-byte/moby/api/types/filters"
     9  	swarmtypes "github.com/Prakhar-Agarwal-byte/moby/api/types/swarm"
    10  	"github.com/Prakhar-Agarwal-byte/moby/client"
    11  	"gotest.tools/v3/poll"
    12  )
    13  
    14  // NoTasksForService verifies that there are no more tasks for the given service
    15  func NoTasksForService(ctx context.Context, client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
    16  	return func(log poll.LogT) poll.Result {
    17  		tasks, err := client.TaskList(ctx, types.TaskListOptions{
    18  			Filters: filters.NewArgs(
    19  				filters.Arg("service", serviceID),
    20  			),
    21  		})
    22  		if err == nil {
    23  			if len(tasks) == 0 {
    24  				return poll.Success()
    25  			}
    26  			if len(tasks) > 0 {
    27  				return poll.Continue("task count for service %s at %d waiting for 0", serviceID, len(tasks))
    28  			}
    29  			return poll.Continue("waiting for tasks for service %s to be deleted", serviceID)
    30  		}
    31  		// TODO we should not use an error as indication that the tasks are gone. There may be other reasons for an error to occur.
    32  		return poll.Success()
    33  	}
    34  }
    35  
    36  // NoTasks verifies that all tasks are gone
    37  func NoTasks(ctx context.Context, client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
    38  	return func(log poll.LogT) poll.Result {
    39  		tasks, err := client.TaskList(ctx, types.TaskListOptions{})
    40  		switch {
    41  		case err != nil:
    42  			return poll.Error(err)
    43  		case len(tasks) == 0:
    44  			return poll.Success()
    45  		default:
    46  			return poll.Continue("waiting for all tasks to be removed: task count at %d", len(tasks))
    47  		}
    48  	}
    49  }
    50  
    51  // RunningTasksCount verifies there are `instances` tasks running for `serviceID`
    52  func RunningTasksCount(ctx context.Context, client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
    53  	return func(log poll.LogT) poll.Result {
    54  		filter := filters.NewArgs()
    55  		filter.Add("service", serviceID)
    56  		tasks, err := client.TaskList(ctx, types.TaskListOptions{
    57  			Filters: filter,
    58  		})
    59  		var running int
    60  		var taskError string
    61  		for _, task := range tasks {
    62  			switch task.Status.State {
    63  			case swarmtypes.TaskStateRunning:
    64  				running++
    65  			case swarmtypes.TaskStateFailed:
    66  				if task.Status.Err != "" {
    67  					taskError = task.Status.Err
    68  				}
    69  			}
    70  		}
    71  
    72  		switch {
    73  		case err != nil:
    74  			return poll.Error(err)
    75  		case running > int(instances):
    76  			return poll.Continue("waiting for tasks to terminate")
    77  		case running < int(instances) && taskError != "":
    78  			return poll.Continue("waiting for tasks to enter run state. task failed with error: %s", taskError)
    79  		case running == int(instances):
    80  			return poll.Success()
    81  		default:
    82  			return poll.Continue("running task count at %d waiting for %d (total tasks: %d)", running, instances, len(tasks))
    83  		}
    84  	}
    85  }
    86  
    87  // JobComplete is a poll function for determining that a ReplicatedJob is
    88  // completed additionally, while polling, it verifies that the job never
    89  // exceeds MaxConcurrent running tasks
    90  func JobComplete(ctx context.Context, client client.CommonAPIClient, service swarmtypes.Service) func(log poll.LogT) poll.Result {
    91  	filter := filters.NewArgs(filters.Arg("service", service.ID))
    92  
    93  	var jobIteration swarmtypes.Version
    94  	if service.JobStatus != nil {
    95  		jobIteration = service.JobStatus.JobIteration
    96  	}
    97  
    98  	maxConcurrent := int(*service.Spec.Mode.ReplicatedJob.MaxConcurrent)
    99  	totalCompletions := int(*service.Spec.Mode.ReplicatedJob.TotalCompletions)
   100  	previousResult := ""
   101  
   102  	return func(log poll.LogT) poll.Result {
   103  		tasks, err := client.TaskList(ctx, types.TaskListOptions{
   104  			Filters: filter,
   105  		})
   106  		if err != nil {
   107  			poll.Error(err)
   108  		}
   109  
   110  		var running int
   111  		var completed int
   112  
   113  		var runningSlot []int
   114  		var runningID []string
   115  
   116  		for _, task := range tasks {
   117  			// make sure the task has the same job iteration
   118  			if task.JobIteration == nil || task.JobIteration.Index != jobIteration.Index {
   119  				continue
   120  			}
   121  			switch task.Status.State {
   122  			case swarmtypes.TaskStateRunning:
   123  				running++
   124  				runningSlot = append(runningSlot, task.Slot)
   125  				runningID = append(runningID, task.ID)
   126  			case swarmtypes.TaskStateComplete:
   127  				completed++
   128  			}
   129  		}
   130  
   131  		switch {
   132  		case running > maxConcurrent:
   133  			return poll.Error(fmt.Errorf(
   134  				"number of running tasks (%v) exceeds max (%v)", running, maxConcurrent,
   135  			))
   136  		case (completed + running) > totalCompletions:
   137  			return poll.Error(fmt.Errorf(
   138  				"number of tasks exceeds total (%v), %v running and %v completed",
   139  				totalCompletions, running, completed,
   140  			))
   141  		case completed == totalCompletions && running == 0:
   142  			return poll.Success()
   143  		default:
   144  			newRes := fmt.Sprintf(
   145  				"Completed: %2d Running: %v\n\t%v",
   146  				completed, runningSlot, runningID,
   147  			)
   148  			if newRes == previousResult {
   149  			} else {
   150  				previousResult = newRes
   151  			}
   152  
   153  			return poll.Continue(
   154  				"Job not yet finished, %v completed and %v running out of %v total",
   155  				completed, running, totalCompletions,
   156  			)
   157  		}
   158  	}
   159  }