github.com/argoproj/argo-cd@v1.8.7/test/e2e/fixture/app/expectation.go (about)

     1  package app
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/argoproj/gitops-engine/pkg/health"
     9  	. "github.com/argoproj/gitops-engine/pkg/sync/common"
    10  	v1 "k8s.io/api/core/v1"
    11  	apierr "k8s.io/apimachinery/pkg/api/errors"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/fields"
    14  
    15  	. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
    16  	"github.com/argoproj/argo-cd/test/e2e/fixture"
    17  )
    18  
    19  type state = string
    20  
    21  const (
    22  	failed    = "failed"
    23  	pending   = "pending"
    24  	succeeded = "succeeded"
    25  )
    26  
    27  type Expectation func(c *Consequences) (state state, message string)
    28  
    29  func OperationPhaseIs(expected OperationPhase) Expectation {
    30  	return func(c *Consequences) (state, string) {
    31  		operationState := c.app().Status.OperationState
    32  		actual := OperationRunning
    33  		if operationState != nil {
    34  			actual = operationState.Phase
    35  		}
    36  		return simple(actual == expected, fmt.Sprintf("operation phase should be %s, is %s", expected, actual))
    37  	}
    38  }
    39  
    40  func OperationMessageContains(text string) Expectation {
    41  	return func(c *Consequences) (state, string) {
    42  		operationState := c.app().Status.OperationState
    43  		actual := ""
    44  		if operationState != nil {
    45  			actual = operationState.Message
    46  		}
    47  		return simple(strings.Contains(actual, text), fmt.Sprintf("operation message should contains '%s', got: '%s'", text, actual))
    48  	}
    49  }
    50  
    51  func simple(success bool, message string) (state, string) {
    52  	if success {
    53  		return succeeded, message
    54  	} else {
    55  		return pending, message
    56  	}
    57  }
    58  
    59  func SyncStatusIs(expected SyncStatusCode) Expectation {
    60  	return func(c *Consequences) (state, string) {
    61  		actual := c.app().Status.Sync.Status
    62  		return simple(actual == expected, fmt.Sprintf("sync status to be %s, is %s", expected, actual))
    63  	}
    64  }
    65  
    66  func Condition(conditionType ApplicationConditionType, conditionMessage string) Expectation {
    67  	return func(c *Consequences) (state, string) {
    68  		got := c.app().Status.Conditions
    69  		message := fmt.Sprintf("condition {%s %s} in %v", conditionType, conditionMessage, got)
    70  		for _, condition := range got {
    71  			if conditionType == condition.Type && strings.Contains(condition.Message, conditionMessage) {
    72  				return succeeded, message
    73  			}
    74  		}
    75  		return pending, message
    76  	}
    77  }
    78  
    79  func NoConditions() Expectation {
    80  	return func(c *Consequences) (state, string) {
    81  		message := "no conditions"
    82  		if len(c.app().Status.Conditions) == 0 {
    83  			return succeeded, message
    84  		}
    85  		return pending, message
    86  	}
    87  }
    88  
    89  func HealthIs(expected health.HealthStatusCode) Expectation {
    90  	return func(c *Consequences) (state, string) {
    91  		actual := c.app().Status.Health.Status
    92  		return simple(actual == expected, fmt.Sprintf("health to should %s, is %s", expected, actual))
    93  	}
    94  }
    95  
    96  func ResourceSyncStatusIs(kind, resource string, expected SyncStatusCode) Expectation {
    97  	return func(c *Consequences) (state, string) {
    98  		actual := c.resource(kind, resource, "").Status
    99  		return simple(actual == expected, fmt.Sprintf("resource '%s/%s' sync status should be %s, is %s", kind, resource, expected, actual))
   100  	}
   101  }
   102  func ResourceSyncStatusWithNamespaceIs(kind, resource, namespace string, expected SyncStatusCode) Expectation {
   103  	return func(c *Consequences) (state, string) {
   104  		actual := c.resource(kind, resource, namespace).Status
   105  		return simple(actual == expected, fmt.Sprintf("resource '%s/%s' sync status should be %s, is %s", kind, resource, expected, actual))
   106  	}
   107  }
   108  func ResourceHealthIs(kind, resource string, expected health.HealthStatusCode) Expectation {
   109  	return func(c *Consequences) (state, string) {
   110  		actual := c.resource(kind, resource, "").Health.Status
   111  		return simple(actual == expected, fmt.Sprintf("resource '%s/%s' health should be %s, is %s", kind, resource, expected, actual))
   112  	}
   113  }
   114  func ResourceHealthWithNamespaceIs(kind, resource, namespace string, expected health.HealthStatusCode) Expectation {
   115  	return func(c *Consequences) (state, string) {
   116  		actual := c.resource(kind, resource, namespace).Health.Status
   117  		return simple(actual == expected, fmt.Sprintf("resource '%s/%s' health should be %s, is %s", kind, resource, expected, actual))
   118  	}
   119  }
   120  func ResourceResultNumbering(num int) Expectation {
   121  	return func(c *Consequences) (state, string) {
   122  		actualNum := len(c.app().Status.OperationState.SyncResult.Resources)
   123  		if actualNum < num {
   124  			return pending, fmt.Sprintf("not enough results yet, want %d, got %d", num, actualNum)
   125  		} else if actualNum == num {
   126  			return succeeded, fmt.Sprintf("right number of results, want %d, got %d", num, actualNum)
   127  		} else {
   128  			return failed, fmt.Sprintf("too many results, want %d, got %d", num, actualNum)
   129  		}
   130  	}
   131  }
   132  
   133  func ResourceResultIs(result ResourceResult) Expectation {
   134  	return func(c *Consequences) (state, string) {
   135  		results := c.app().Status.OperationState.SyncResult.Resources
   136  		for _, res := range results {
   137  			if *res == result {
   138  				return succeeded, fmt.Sprintf("found resource result %v", result)
   139  			}
   140  		}
   141  		return pending, fmt.Sprintf("waiting for resource result %v in %v", result, results)
   142  	}
   143  }
   144  
   145  func DoesNotExist() Expectation {
   146  	return func(c *Consequences) (state, string) {
   147  		_, err := c.get()
   148  		if err != nil {
   149  			if apierr.IsNotFound(err) {
   150  				return succeeded, "app does not exist"
   151  			}
   152  			return failed, err.Error()
   153  		}
   154  		return pending, "app should not exist"
   155  	}
   156  }
   157  
   158  func Pod(predicate func(p v1.Pod) bool) Expectation {
   159  	return func(c *Consequences) (state, string) {
   160  		pods, err := pods()
   161  		if err != nil {
   162  			return failed, err.Error()
   163  		}
   164  		for _, pod := range pods.Items {
   165  			if predicate(pod) {
   166  				return succeeded, fmt.Sprintf("pod predicate matched pod named '%s'", pod.GetName())
   167  			}
   168  		}
   169  		return pending, "pod predicate does not match pods"
   170  	}
   171  }
   172  
   173  func NotPod(predicate func(p v1.Pod) bool) Expectation {
   174  	return func(c *Consequences) (state, string) {
   175  		pods, err := pods()
   176  		if err != nil {
   177  			return failed, err.Error()
   178  		}
   179  		for _, pod := range pods.Items {
   180  			if predicate(pod) {
   181  				return pending, fmt.Sprintf("pod predicate matched pod named '%s'", pod.GetName())
   182  			}
   183  		}
   184  		return succeeded, "pod predicate did not match any pod"
   185  	}
   186  }
   187  
   188  func pods() (*v1.PodList, error) {
   189  	fixture.KubeClientset.CoreV1()
   190  	pods, err := fixture.KubeClientset.CoreV1().Pods(fixture.DeploymentNamespace()).List(context.Background(), metav1.ListOptions{})
   191  	return pods, err
   192  }
   193  
   194  func Event(reason string, message string) Expectation {
   195  	return func(c *Consequences) (state, string) {
   196  		list, err := fixture.KubeClientset.CoreV1().Events(fixture.ArgoCDNamespace).List(context.Background(), metav1.ListOptions{
   197  			FieldSelector: fields.SelectorFromSet(map[string]string{
   198  				"involvedObject.name":      c.context.name,
   199  				"involvedObject.namespace": fixture.ArgoCDNamespace,
   200  			}).String(),
   201  		})
   202  		if err != nil {
   203  			return failed, err.Error()
   204  		}
   205  
   206  		for i := range list.Items {
   207  			event := list.Items[i]
   208  			if event.Reason == reason && strings.Contains(event.Message, message) {
   209  				return succeeded, fmt.Sprintf("found event with reason=%s; message=%s", reason, message)
   210  			}
   211  		}
   212  		return failed, fmt.Sprintf("unable to find event with reason=%s; message=%s", reason, message)
   213  	}
   214  }
   215  
   216  // asserts that the last command was successful
   217  func Success(message string) Expectation {
   218  	return func(c *Consequences) (state, string) {
   219  		if c.actions.lastError != nil {
   220  			return failed, "error"
   221  		}
   222  		if !strings.Contains(c.actions.lastOutput, message) {
   223  			return failed, fmt.Sprintf("output did not contain '%s'", message)
   224  		}
   225  		return succeeded, fmt.Sprintf("no error and output contained '%s'", message)
   226  	}
   227  }
   228  
   229  // asserts that the last command was an error with substring match
   230  func Error(message, err string) Expectation {
   231  	return func(c *Consequences) (state, string) {
   232  		if c.actions.lastError == nil {
   233  			return failed, "no error"
   234  		}
   235  		if !strings.Contains(c.actions.lastOutput, message) {
   236  			return failed, fmt.Sprintf("output does not contain '%s'", message)
   237  		}
   238  		if !strings.Contains(c.actions.lastError.Error(), err) {
   239  			return failed, fmt.Sprintf("error does not contain '%s'", message)
   240  		}
   241  		return succeeded, fmt.Sprintf("error '%s'", message)
   242  	}
   243  }