...

Source file src/sigs.k8s.io/cli-utils/pkg/kstatus/status/status_compute_test.go

Documentation: sigs.k8s.io/cli-utils/pkg/kstatus/status

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package status
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	corev1 "k8s.io/api/core/v1"
    13  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    14  	"sigs.k8s.io/yaml"
    15  )
    16  
    17  func y2u(t *testing.T, spec string) *unstructured.Unstructured {
    18  	j, err := yaml.YAMLToJSON([]byte(spec))
    19  	assert.NoError(t, err)
    20  	u, _, err := unstructured.UnstructuredJSONScheme.Decode(j, nil, nil)
    21  	assert.NoError(t, err)
    22  	return u.(*unstructured.Unstructured)
    23  }
    24  
    25  type testSpec struct {
    26  	spec                 string
    27  	expectedStatus       Status
    28  	expectedConditions   []Condition
    29  	absentConditionTypes []ConditionType
    30  }
    31  
    32  func runStatusTest(t *testing.T, tc testSpec) {
    33  	res, err := Compute(y2u(t, tc.spec))
    34  	assert.NoError(t, err)
    35  	assert.Equal(t, tc.expectedStatus, res.Status)
    36  
    37  	for _, expectedCondition := range tc.expectedConditions {
    38  		found := false
    39  		for _, condition := range res.Conditions {
    40  			if condition.Type != expectedCondition.Type {
    41  				continue
    42  			}
    43  			found = true
    44  			assert.Equal(t, expectedCondition.Status, condition.Status)
    45  			assert.Equal(t, expectedCondition.Reason, condition.Reason)
    46  		}
    47  		if !found {
    48  			t.Errorf("Expected condition of type %s, but didn't find it", expectedCondition.Type)
    49  		}
    50  	}
    51  
    52  	for _, absentConditionType := range tc.absentConditionTypes {
    53  		for _, condition := range res.Conditions {
    54  			if condition.Type == absentConditionType {
    55  				t.Errorf("Expected condition %s to be absent, but found it", absentConditionType)
    56  			}
    57  		}
    58  	}
    59  }
    60  
    61  var podNoStatus = `
    62  apiVersion: v1
    63  kind: Pod
    64  metadata:
    65     generation: 1
    66     name: test
    67  `
    68  
    69  var podReady = `
    70  apiVersion: v1
    71  kind: Pod
    72  metadata:
    73     generation: 1
    74     name: test
    75     namespace: qual
    76  status:
    77     conditions:
    78      - type: Ready 
    79        status: "True"
    80     phase: Running
    81  `
    82  
    83  var podCompletedOK = `
    84  apiVersion: v1
    85  kind: Pod
    86  metadata:
    87     generation: 1
    88     name: test
    89     namespace: qual
    90  status:
    91     phase: Succeeded
    92     conditions:
    93      - type: Ready 
    94        status: "False"
    95        reason: PodCompleted
    96  
    97  `
    98  
    99  var podCompletedFail = `
   100  apiVersion: v1
   101  kind: Pod
   102  metadata:
   103     generation: 1
   104     name: test
   105     namespace: qual
   106  status:
   107     phase: Failed
   108     conditions:
   109      - type: Ready 
   110        status: "False"
   111        reason: PodCompleted
   112  `
   113  
   114  var podBeingScheduled = `
   115  apiVersion: v1
   116  kind: Pod
   117  metadata:
   118     creationTimestamp: %s
   119     generation: 1
   120     name: test
   121     namespace: qual
   122  status:
   123     phase: Pending
   124     conditions:
   125      - type: PodScheduled 
   126        status: "False"
   127        reason: Unschedulable
   128  `
   129  
   130  var podUnschedulable = `
   131  apiVersion: v1
   132  kind: Pod
   133  metadata:
   134     generation: 1
   135     name: test
   136     namespace: qual
   137  status:
   138     phase: Pending
   139     conditions:
   140      - type: PodScheduled 
   141        status: "False"
   142        reason: Unschedulable
   143  `
   144  
   145  var podCrashLooping = `
   146  apiVersion: v1
   147  kind: Pod
   148  metadata:
   149     generation: 1
   150     name: test
   151     namespace: qual
   152  status:
   153     phase: Running
   154     conditions:
   155      - type: PodScheduled 
   156        status: "False"
   157        reason: Unschedulable
   158     containerStatuses:
   159      - name: nginx
   160        state:
   161           waiting:
   162              reason: CrashLoopBackOff
   163  `
   164  
   165  // Test coverage using GetConditions
   166  func TestPodStatus(t *testing.T) {
   167  	testCases := map[string]testSpec{
   168  		"podNoStatus": {
   169  			spec:           podNoStatus,
   170  			expectedStatus: InProgressStatus,
   171  			expectedConditions: []Condition{{
   172  				Type:   ConditionReconciling,
   173  				Status: corev1.ConditionTrue,
   174  				Reason: "PodNotObserved",
   175  			}},
   176  			absentConditionTypes: []ConditionType{
   177  				ConditionStalled,
   178  			},
   179  		},
   180  		"podReady": {
   181  			spec:               podReady,
   182  			expectedStatus:     CurrentStatus,
   183  			expectedConditions: []Condition{},
   184  			absentConditionTypes: []ConditionType{
   185  				ConditionReconciling,
   186  				ConditionStalled,
   187  			},
   188  		},
   189  		"podCompletedSuccessfully": {
   190  			spec:               podCompletedOK,
   191  			expectedStatus:     CurrentStatus,
   192  			expectedConditions: []Condition{},
   193  			absentConditionTypes: []ConditionType{
   194  				ConditionReconciling,
   195  				ConditionStalled,
   196  			},
   197  		},
   198  		"podCompletedFailed": {
   199  			spec:               podCompletedFail,
   200  			expectedStatus:     CurrentStatus,
   201  			expectedConditions: []Condition{},
   202  			absentConditionTypes: []ConditionType{
   203  				ConditionReconciling,
   204  				ConditionStalled,
   205  			},
   206  		},
   207  		"podBeingScheduled": {
   208  			spec:           fmt.Sprintf(podBeingScheduled, time.Now().Format(time.RFC3339)),
   209  			expectedStatus: InProgressStatus,
   210  			expectedConditions: []Condition{
   211  				{
   212  					Type:   ConditionReconciling,
   213  					Status: corev1.ConditionTrue,
   214  					Reason: "PodNotScheduled",
   215  				},
   216  			},
   217  			absentConditionTypes: []ConditionType{
   218  				ConditionStalled,
   219  			},
   220  		},
   221  		"podUnschedulable": {
   222  			spec:           podUnschedulable,
   223  			expectedStatus: FailedStatus,
   224  			expectedConditions: []Condition{
   225  				{
   226  					Type:   ConditionStalled,
   227  					Status: corev1.ConditionTrue,
   228  					Reason: "PodUnschedulable",
   229  				},
   230  			},
   231  			absentConditionTypes: []ConditionType{
   232  				ConditionReconciling,
   233  			},
   234  		},
   235  		"podCrashLooping": {
   236  			spec:           podCrashLooping,
   237  			expectedStatus: FailedStatus,
   238  			expectedConditions: []Condition{
   239  				{
   240  					Type:   ConditionStalled,
   241  					Status: corev1.ConditionTrue,
   242  					Reason: "ContainerCrashLooping",
   243  				},
   244  			},
   245  			absentConditionTypes: []ConditionType{
   246  				ConditionReconciling,
   247  			},
   248  		},
   249  	}
   250  
   251  	for tn, tc := range testCases {
   252  		tc := tc
   253  		t.Run(tn, func(t *testing.T) {
   254  			runStatusTest(t, tc)
   255  		})
   256  	}
   257  }
   258  
   259  var pvcNoStatus = `
   260  apiVersion: v1
   261  kind: PersistentVolumeClaim
   262  metadata:
   263     generation: 1
   264     name: test
   265  `
   266  var pvcBound = `
   267  apiVersion: v1
   268  kind: PersistentVolumeClaim
   269  metadata:
   270     generation: 1
   271     name: test
   272     namespace: qual
   273  status:
   274     phase: Bound
   275  `
   276  
   277  func TestPVCStatus(t *testing.T) {
   278  	testCases := map[string]testSpec{
   279  		"pvcNoStatus": {
   280  			spec:           pvcNoStatus,
   281  			expectedStatus: InProgressStatus,
   282  			expectedConditions: []Condition{{
   283  				Type:   ConditionReconciling,
   284  				Status: corev1.ConditionTrue,
   285  				Reason: "NotBound",
   286  			}},
   287  			absentConditionTypes: []ConditionType{
   288  				ConditionStalled,
   289  			},
   290  		},
   291  		"pvcBound": {
   292  			spec:               pvcBound,
   293  			expectedStatus:     CurrentStatus,
   294  			expectedConditions: []Condition{},
   295  			absentConditionTypes: []ConditionType{
   296  				ConditionStalled,
   297  				ConditionReconciling,
   298  			},
   299  		},
   300  	}
   301  
   302  	for tn, tc := range testCases {
   303  		tc := tc
   304  		t.Run(tn, func(t *testing.T) {
   305  			runStatusTest(t, tc)
   306  		})
   307  	}
   308  }
   309  
   310  var stsNoStatus = `
   311  apiVersion: apps/v1
   312  kind: StatefulSet
   313  metadata:
   314     generation: 1
   315     name: test
   316  `
   317  var stsBadStatus = `
   318  apiVersion: apps/v1
   319  kind: StatefulSet
   320  metadata:
   321     generation: 1
   322     name: test
   323     namespace: qual
   324  status:
   325     observedGeneration: 1
   326     currentReplicas: 1
   327  `
   328  
   329  var stsOK = `
   330  apiVersion: apps/v1
   331  kind: StatefulSet
   332  metadata:
   333     generation: 1
   334     name: test
   335     namespace: qual
   336  spec:
   337     replicas: 4
   338  status:
   339     observedGeneration: 1
   340     currentReplicas: 4
   341     readyReplicas: 4
   342     replicas: 4
   343  `
   344  
   345  var stsLessReady = `
   346  apiVersion: apps/v1
   347  kind: StatefulSet
   348  metadata:
   349     generation: 1
   350     name: test
   351     namespace: qual
   352  spec:
   353     replicas: 4
   354  status:
   355     observedGeneration: 1
   356     currentReplicas: 4
   357     readyReplicas: 2
   358     replicas: 4
   359  `
   360  var stsLessCurrent = `
   361  apiVersion: apps/v1
   362  kind: StatefulSet
   363  metadata:
   364     generation: 1
   365     name: test
   366     namespace: qual
   367  spec:
   368     replicas: 4
   369  status:
   370     observedGeneration: 1
   371     currentReplicas: 2
   372     readyReplicas: 4
   373     replicas: 4
   374  `
   375  var stsExtraPods = `
   376  apiVersion: apps/v1
   377  kind: StatefulSet
   378  metadata:
   379     generation: 1
   380     name: test
   381     namespace: qual
   382  spec:
   383     replicas: 4
   384  status:
   385     observedGeneration: 1
   386     currentReplicas: 4
   387     readyReplicas: 4
   388     replicas: 8
   389  `
   390  
   391  func TestStsStatus(t *testing.T) {
   392  	testCases := map[string]testSpec{
   393  		"stsNoStatus": {
   394  			spec:           stsNoStatus,
   395  			expectedStatus: InProgressStatus,
   396  			expectedConditions: []Condition{{
   397  				Type:   ConditionReconciling,
   398  				Status: corev1.ConditionTrue,
   399  				Reason: "LessReplicas",
   400  			}},
   401  			absentConditionTypes: []ConditionType{
   402  				ConditionStalled,
   403  			},
   404  		},
   405  		"stsBadStatus": {
   406  			spec:           stsBadStatus,
   407  			expectedStatus: InProgressStatus,
   408  			expectedConditions: []Condition{{
   409  				Type:   ConditionReconciling,
   410  				Status: corev1.ConditionTrue,
   411  				Reason: "LessReplicas",
   412  			}},
   413  			absentConditionTypes: []ConditionType{
   414  				ConditionStalled,
   415  			},
   416  		},
   417  		"stsOK": {
   418  			spec:               stsOK,
   419  			expectedStatus:     CurrentStatus,
   420  			expectedConditions: []Condition{},
   421  			absentConditionTypes: []ConditionType{
   422  				ConditionStalled,
   423  				ConditionReconciling,
   424  			},
   425  		},
   426  		"stsLessReady": {
   427  			spec:           stsLessReady,
   428  			expectedStatus: InProgressStatus,
   429  			expectedConditions: []Condition{{
   430  				Type:   ConditionReconciling,
   431  				Status: corev1.ConditionTrue,
   432  				Reason: "LessReady",
   433  			}},
   434  			absentConditionTypes: []ConditionType{
   435  				ConditionStalled,
   436  			},
   437  		},
   438  		"stsLessCurrent": {
   439  			spec:           stsLessCurrent,
   440  			expectedStatus: InProgressStatus,
   441  			expectedConditions: []Condition{{
   442  				Type:   ConditionReconciling,
   443  				Status: corev1.ConditionTrue,
   444  				Reason: "LessCurrent",
   445  			}},
   446  			absentConditionTypes: []ConditionType{
   447  				ConditionStalled,
   448  			},
   449  		},
   450  		"stsExtraPods": {
   451  			spec:           stsExtraPods,
   452  			expectedStatus: InProgressStatus,
   453  			expectedConditions: []Condition{{
   454  				Type:   ConditionReconciling,
   455  				Status: corev1.ConditionTrue,
   456  				Reason: "ExtraPods",
   457  			}},
   458  			absentConditionTypes: []ConditionType{
   459  				ConditionStalled,
   460  			},
   461  		},
   462  	}
   463  
   464  	for tn, tc := range testCases {
   465  		tc := tc
   466  		t.Run(tn, func(t *testing.T) {
   467  			runStatusTest(t, tc)
   468  		})
   469  	}
   470  }
   471  
   472  var dsNoStatus = `
   473  apiVersion: apps/v1
   474  kind: DaemonSet
   475  metadata:
   476     name: test
   477     generation: 1
   478  `
   479  var dsBadStatus = `
   480  apiVersion: apps/v1
   481  kind: DaemonSet
   482  metadata:
   483     name: test
   484     namespace: qual
   485     generation: 1
   486  status:
   487     observedGeneration: 1
   488     currentReplicas: 1
   489  `
   490  
   491  var dsOK = `
   492  apiVersion: apps/v1
   493  kind: DaemonSet
   494  metadata:
   495     name: test
   496     namespace: qual
   497     generation: 1
   498  status:
   499     desiredNumberScheduled: 4
   500     currentNumberScheduled: 4
   501     updatedNumberScheduled: 4
   502     numberAvailable: 4
   503     numberReady: 4
   504     observedGeneration: 1
   505  `
   506  var dsDifferentGeneration = `
   507  apiVersion: apps/v1
   508  kind: DaemonSet
   509  metadata:
   510     name: test
   511     namespace: qual
   512     generation: 2
   513  status:
   514     desiredNumberScheduled: 4
   515     currentNumberScheduled: 4
   516     updatedNumberScheduled: 4
   517     numberAvailable: 4
   518     numberReady: 4
   519     observedGeneration: 1
   520  `
   521  
   522  var dsLessReady = `
   523  apiVersion: apps/v1
   524  kind: DaemonSet
   525  metadata:
   526     name: test
   527     namespace: qual
   528     generation: 1
   529  status:
   530     observedGeneration: 1
   531     desiredNumberScheduled: 4
   532     currentNumberScheduled: 4
   533     updatedNumberScheduled: 4
   534     numberAvailable: 4
   535     numberReady: 2
   536  `
   537  var dsLessAvailable = `
   538  apiVersion: apps/v1
   539  kind: DaemonSet
   540  metadata:
   541     name: test
   542     namespace: qual
   543     generation: 1
   544  status:
   545     observedGeneration: 1
   546     desiredNumberScheduled: 4
   547     currentNumberScheduled: 4
   548     updatedNumberScheduled: 4
   549     numberAvailable: 2
   550     numberReady: 4
   551  `
   552  
   553  func TestDaemonsetStatus(t *testing.T) {
   554  	testCases := map[string]testSpec{
   555  		"dsNoStatus": {
   556  			spec:           dsNoStatus,
   557  			expectedStatus: InProgressStatus,
   558  			expectedConditions: []Condition{{
   559  				Type:   ConditionReconciling,
   560  				Status: corev1.ConditionTrue,
   561  				Reason: "NoObservedGeneration",
   562  			}},
   563  			absentConditionTypes: []ConditionType{
   564  				ConditionStalled,
   565  			},
   566  		},
   567  		"dsBadStatus": {
   568  			spec:           dsBadStatus,
   569  			expectedStatus: InProgressStatus,
   570  			expectedConditions: []Condition{{
   571  				Type:   ConditionReconciling,
   572  				Status: corev1.ConditionTrue,
   573  				Reason: "NoDesiredNumber",
   574  			}},
   575  			absentConditionTypes: []ConditionType{
   576  				ConditionStalled,
   577  			},
   578  		},
   579  		"dsOK": {
   580  			spec:               dsOK,
   581  			expectedStatus:     CurrentStatus,
   582  			expectedConditions: []Condition{},
   583  			absentConditionTypes: []ConditionType{
   584  				ConditionStalled,
   585  				ConditionReconciling,
   586  			},
   587  		},
   588  		"dsLessReady": {
   589  			spec:           dsLessReady,
   590  			expectedStatus: InProgressStatus,
   591  			expectedConditions: []Condition{{
   592  				Type:   ConditionReconciling,
   593  				Status: corev1.ConditionTrue,
   594  				Reason: "LessReady",
   595  			}},
   596  			absentConditionTypes: []ConditionType{
   597  				ConditionStalled,
   598  			},
   599  		},
   600  		"dsDifferentGeneration": {
   601  			spec:           dsDifferentGeneration,
   602  			expectedStatus: InProgressStatus,
   603  			expectedConditions: []Condition{{
   604  				Type:   ConditionReconciling,
   605  				Status: corev1.ConditionTrue,
   606  				Reason: "LatestGenerationNotObserved",
   607  			}},
   608  			absentConditionTypes: []ConditionType{
   609  				ConditionStalled,
   610  			},
   611  		},
   612  		"dsLessAvailable": {
   613  			spec:           dsLessAvailable,
   614  			expectedStatus: InProgressStatus,
   615  			expectedConditions: []Condition{{
   616  				Type:   ConditionReconciling,
   617  				Status: corev1.ConditionTrue,
   618  				Reason: "LessAvailable",
   619  			}},
   620  			absentConditionTypes: []ConditionType{
   621  				ConditionStalled,
   622  			},
   623  		},
   624  	}
   625  
   626  	for tn, tc := range testCases {
   627  		tc := tc
   628  		t.Run(tn, func(t *testing.T) {
   629  			runStatusTest(t, tc)
   630  		})
   631  	}
   632  }
   633  
   634  var depNoStatus = `
   635  apiVersion: apps/v1
   636  kind: Deployment
   637  metadata:
   638     name: test
   639     generation: 1
   640  `
   641  
   642  var depOK = `
   643  apiVersion: apps/v1
   644  kind: Deployment
   645  metadata:
   646     name: test
   647     generation: 1
   648     namespace: qual
   649  status:
   650     observedGeneration: 1
   651     updatedReplicas: 1
   652     readyReplicas: 1
   653     availableReplicas: 1
   654     replicas: 1
   655     conditions:
   656      - type: Progressing 
   657        status: "True"
   658        reason: NewReplicaSetAvailable
   659      - type: Available 
   660        status: "True"
   661  `
   662  
   663  var depNotProgressing = `
   664  apiVersion: apps/v1
   665  kind: Deployment
   666  metadata:
   667     name: test
   668     generation: 1
   669     namespace: qual
   670  spec:
   671     progressDeadlineSeconds: 45
   672  status:
   673     observedGeneration: 1
   674     updatedReplicas: 1
   675     readyReplicas: 1
   676     availableReplicas: 1
   677     replicas: 1
   678     observedGeneration: 1
   679     conditions:
   680      - type: Progressing 
   681        status: "False"
   682        reason: Some reason
   683      - type: Available 
   684        status: "True"
   685  `
   686  
   687  var depNoProgressDeadlineSeconds = `
   688  apiVersion: apps/v1
   689  kind: Deployment
   690  metadata:
   691     name: test
   692     generation: 1
   693     namespace: qual
   694  status:
   695     observedGeneration: 1
   696     updatedReplicas: 1
   697     readyReplicas: 1
   698     availableReplicas: 1
   699     replicas: 1
   700     observedGeneration: 1
   701     conditions:
   702      - type: Available
   703        status: "True"
   704  `
   705  
   706  var depNotAvailable = `
   707  apiVersion: apps/v1
   708  kind: Deployment
   709  metadata:
   710     name: test
   711     generation: 1
   712     namespace: qual
   713  status:
   714     observedGeneration: 1
   715     updatedReplicas: 1
   716     readyReplicas: 1
   717     availableReplicas: 1
   718     replicas: 1
   719     observedGeneration: 1
   720     conditions:
   721      - type: Progressing 
   722        status: "True"
   723        reason: NewReplicaSetAvailable
   724      - type: Available 
   725        status: "False"
   726  `
   727  
   728  func TestDeploymentStatus(t *testing.T) {
   729  	testCases := map[string]testSpec{
   730  		"depNoStatus": {
   731  			spec:           depNoStatus,
   732  			expectedStatus: InProgressStatus,
   733  			expectedConditions: []Condition{{
   734  				Type:   ConditionReconciling,
   735  				Status: corev1.ConditionTrue,
   736  				Reason: "LessReplicas",
   737  			}},
   738  			absentConditionTypes: []ConditionType{
   739  				ConditionStalled,
   740  			},
   741  		},
   742  		"depOK": {
   743  			spec:               depOK,
   744  			expectedStatus:     CurrentStatus,
   745  			expectedConditions: []Condition{},
   746  			absentConditionTypes: []ConditionType{
   747  				ConditionStalled,
   748  				ConditionReconciling,
   749  			},
   750  		},
   751  		"depNotProgressing": {
   752  			spec:           depNotProgressing,
   753  			expectedStatus: InProgressStatus,
   754  			expectedConditions: []Condition{{
   755  				Type:   ConditionReconciling,
   756  				Status: corev1.ConditionTrue,
   757  				Reason: "ReplicaSetNotAvailable",
   758  			}},
   759  			absentConditionTypes: []ConditionType{
   760  				ConditionStalled,
   761  			},
   762  		},
   763  		"depNoProgressDeadlineSeconds": {
   764  			spec:               depNoProgressDeadlineSeconds,
   765  			expectedStatus:     CurrentStatus,
   766  			expectedConditions: []Condition{},
   767  			absentConditionTypes: []ConditionType{
   768  				ConditionStalled,
   769  				ConditionReconciling,
   770  			},
   771  		},
   772  		"depNotAvailable": {
   773  			spec:           depNotAvailable,
   774  			expectedStatus: InProgressStatus,
   775  			expectedConditions: []Condition{{
   776  				Type:   ConditionReconciling,
   777  				Status: corev1.ConditionTrue,
   778  				Reason: "DeploymentNotAvailable",
   779  			}},
   780  			absentConditionTypes: []ConditionType{
   781  				ConditionStalled,
   782  			},
   783  		},
   784  	}
   785  
   786  	for tn, tc := range testCases {
   787  		tc := tc
   788  		t.Run(tn, func(t *testing.T) {
   789  			runStatusTest(t, tc)
   790  		})
   791  	}
   792  }
   793  
   794  var rsNoStatus = `
   795  apiVersion: apps/v1
   796  kind: ReplicaSet
   797  metadata:
   798     name: test
   799     generation: 1
   800  `
   801  
   802  var rsOK1 = `
   803  apiVersion: apps/v1
   804  kind: ReplicaSet
   805  metadata:
   806     name: test
   807     namespace: qual
   808     generation: 1
   809  spec:
   810     replicas: 2
   811  status:
   812     observedGeneration: 1
   813     replicas: 2
   814     readyReplicas: 2
   815     availableReplicas: 2
   816     fullyLabeledReplicas: 2
   817     conditions:
   818      - type: ReplicaFailure 
   819        status: "False"
   820  `
   821  
   822  var rsOK2 = `
   823  apiVersion: apps/v1
   824  kind: ReplicaSet
   825  metadata:
   826     name: test
   827     namespace: qual
   828     generation: 1
   829  spec:
   830     replicas: 2
   831  status:
   832     observedGeneration: 1
   833     fullyLabeledReplicas: 2
   834     replicas: 2
   835     readyReplicas: 2
   836     availableReplicas: 2
   837  `
   838  
   839  var rsLessReady = `
   840  apiVersion: apps/v1
   841  kind: ReplicaSet
   842  metadata:
   843     name: test
   844     namespace: qual
   845     generation: 1
   846  spec:
   847     replicas: 4
   848  status:
   849     observedGeneration: 1
   850     replicas: 4
   851     readyReplicas: 2
   852     availableReplicas: 4
   853     fullyLabeledReplicas: 4
   854  `
   855  
   856  var rsLessAvailable = `
   857  apiVersion: apps/v1
   858  kind: ReplicaSet
   859  metadata:
   860     name: test
   861     namespace: qual
   862     generation: 1
   863  spec:
   864     replicas: 4
   865  status:
   866     observedGeneration: 1
   867     replicas: 4
   868     readyReplicas: 4
   869     availableReplicas: 2
   870     fullyLabeledReplicas: 4
   871  `
   872  
   873  var rsReplicaFailure = `
   874  apiVersion: apps/v1
   875  kind: ReplicaSet
   876  metadata:
   877     name: test
   878     namespace: qual
   879     generation: 1
   880  spec:
   881     replicas: 4
   882  status:
   883     observedGeneration: 1
   884     replicas: 4
   885     readyReplicas: 4
   886     fullyLabeledReplicas: 4
   887     availableReplicas: 4
   888     conditions:
   889      - type: ReplicaFailure 
   890        status: "True"
   891  `
   892  
   893  func TestReplicasetStatus(t *testing.T) {
   894  	testCases := map[string]testSpec{
   895  		"rsNoStatus": {
   896  			spec:           rsNoStatus,
   897  			expectedStatus: InProgressStatus,
   898  			expectedConditions: []Condition{{
   899  				Type:   ConditionReconciling,
   900  				Status: corev1.ConditionTrue,
   901  				Reason: "LessLabelled",
   902  			}},
   903  			absentConditionTypes: []ConditionType{
   904  				ConditionStalled,
   905  			},
   906  		},
   907  		"rsOK1": {
   908  			spec:               rsOK1,
   909  			expectedStatus:     CurrentStatus,
   910  			expectedConditions: []Condition{},
   911  			absentConditionTypes: []ConditionType{
   912  				ConditionStalled,
   913  				ConditionReconciling,
   914  			},
   915  		},
   916  		"rsOK2": {
   917  			spec:               rsOK2,
   918  			expectedStatus:     CurrentStatus,
   919  			expectedConditions: []Condition{},
   920  			absentConditionTypes: []ConditionType{
   921  				ConditionStalled,
   922  				ConditionReconciling,
   923  			},
   924  		},
   925  		"rsLessAvailable": {
   926  			spec:           rsLessAvailable,
   927  			expectedStatus: InProgressStatus,
   928  			expectedConditions: []Condition{{
   929  				Type:   ConditionReconciling,
   930  				Status: corev1.ConditionTrue,
   931  				Reason: "LessAvailable",
   932  			}},
   933  			absentConditionTypes: []ConditionType{
   934  				ConditionStalled,
   935  			},
   936  		},
   937  		"rsLessReady": {
   938  			spec:           rsLessReady,
   939  			expectedStatus: InProgressStatus,
   940  			expectedConditions: []Condition{{
   941  				Type:   ConditionReconciling,
   942  				Status: corev1.ConditionTrue,
   943  				Reason: "LessReady",
   944  			}},
   945  			absentConditionTypes: []ConditionType{
   946  				ConditionStalled,
   947  			},
   948  		},
   949  		"rsReplicaFailure": {
   950  			spec:           rsReplicaFailure,
   951  			expectedStatus: InProgressStatus,
   952  			expectedConditions: []Condition{{
   953  				Type:   ConditionReconciling,
   954  				Status: corev1.ConditionTrue,
   955  				Reason: "ReplicaFailure",
   956  			}},
   957  			absentConditionTypes: []ConditionType{
   958  				ConditionStalled,
   959  			},
   960  		},
   961  	}
   962  
   963  	for tn, tc := range testCases {
   964  		tc := tc
   965  		t.Run(tn, func(t *testing.T) {
   966  			runStatusTest(t, tc)
   967  		})
   968  	}
   969  }
   970  
   971  var pdbNotObserved = `
   972  apiVersion: policy/v1beta1
   973  kind: PodDisruptionBudget
   974  metadata:
   975     generation: 2
   976     name: test
   977     namespace: qual
   978  status:
   979     observedGeneration: 1
   980  `
   981  
   982  var pdbObserved = `
   983  apiVersion: policy/v1beta1
   984  kind: PodDisruptionBudget
   985  metadata:
   986     generation: 1
   987     name: test
   988     namespace: qual
   989  status:
   990     observedGeneration: 1
   991  `
   992  
   993  func TestPDBStatus(t *testing.T) {
   994  	testCases := map[string]testSpec{
   995  		"pdbNotObserved": {
   996  			spec:           pdbNotObserved,
   997  			expectedStatus: InProgressStatus,
   998  			expectedConditions: []Condition{{
   999  				Type:   ConditionReconciling,
  1000  				Status: corev1.ConditionTrue,
  1001  				Reason: "LatestGenerationNotObserved",
  1002  			}},
  1003  			absentConditionTypes: []ConditionType{
  1004  				ConditionStalled,
  1005  			},
  1006  		},
  1007  		"pdbObserved": {
  1008  			spec:               pdbObserved,
  1009  			expectedStatus:     CurrentStatus,
  1010  			expectedConditions: []Condition{},
  1011  			absentConditionTypes: []ConditionType{
  1012  				ConditionStalled,
  1013  				ConditionReconciling,
  1014  			},
  1015  		},
  1016  	}
  1017  
  1018  	for tn, tc := range testCases {
  1019  		tc := tc
  1020  		t.Run(tn, func(t *testing.T) {
  1021  			runStatusTest(t, tc)
  1022  		})
  1023  	}
  1024  }
  1025  
  1026  var crdNoStatus = `
  1027  apiVersion: something/v1
  1028  kind: MyCR
  1029  metadata:
  1030     generation: 1
  1031     name: test
  1032     namespace: qual
  1033  `
  1034  
  1035  var crdMismatchStatusGeneration = `
  1036  apiVersion: something/v1
  1037  kind: MyCR
  1038  metadata:
  1039     name: test
  1040     namespace: qual
  1041     generation: 2
  1042  status:
  1043     observedGeneration: 1
  1044  `
  1045  
  1046  var crdReady = `
  1047  apiVersion: something/v1
  1048  kind: MyCR
  1049  metadata:
  1050     name: test
  1051     namespace: qual
  1052     generation: 1
  1053  status:
  1054     conditions:
  1055      - type: Ready 
  1056        status: "True"
  1057        message: All looks ok
  1058        reason: AllOk
  1059  `
  1060  
  1061  var crdNotReady = `
  1062  apiVersion: something/v1
  1063  kind: MyCR
  1064  metadata:
  1065     generation: 1
  1066     name: test
  1067     namespace: qual
  1068  status:
  1069     observedGeneration: 1
  1070     conditions:
  1071      - type: Ready 
  1072        status: "False"
  1073        reason: NotReadyYet
  1074  `
  1075  
  1076  var crdNoCondition = `
  1077  apiVersion: something/v1
  1078  kind: MyCR
  1079  metadata:
  1080     name: test
  1081     namespace: qual
  1082     generation: 1
  1083  status:
  1084     conditions:
  1085      - type: SomeCondition 
  1086        status: "False"
  1087  `
  1088  
  1089  func TestCRDGenericStatus(t *testing.T) {
  1090  	testCases := map[string]testSpec{
  1091  		"crdNoStatus": {
  1092  			spec:               crdNoStatus,
  1093  			expectedStatus:     CurrentStatus,
  1094  			expectedConditions: []Condition{},
  1095  			absentConditionTypes: []ConditionType{
  1096  				ConditionStalled,
  1097  				ConditionReconciling,
  1098  			},
  1099  		},
  1100  		"crdReady": {
  1101  			spec:               crdReady,
  1102  			expectedStatus:     CurrentStatus,
  1103  			expectedConditions: []Condition{},
  1104  			absentConditionTypes: []ConditionType{
  1105  				ConditionStalled,
  1106  				ConditionReconciling,
  1107  			},
  1108  		},
  1109  		"crdNotReady": {
  1110  			spec:           crdNotReady,
  1111  			expectedStatus: InProgressStatus,
  1112  			expectedConditions: []Condition{
  1113  				{
  1114  					Type:   ConditionReconciling,
  1115  					Status: corev1.ConditionTrue,
  1116  					Reason: "NotReadyYet",
  1117  				},
  1118  			},
  1119  			absentConditionTypes: []ConditionType{
  1120  				ConditionStalled,
  1121  			},
  1122  		},
  1123  		"crdNoCondition": {
  1124  			spec:               crdNoCondition,
  1125  			expectedStatus:     CurrentStatus,
  1126  			expectedConditions: []Condition{},
  1127  			absentConditionTypes: []ConditionType{
  1128  				ConditionStalled,
  1129  				ConditionReconciling,
  1130  			},
  1131  		},
  1132  		"crdMismatchStatusGeneration": {
  1133  			spec:           crdMismatchStatusGeneration,
  1134  			expectedStatus: InProgressStatus,
  1135  			expectedConditions: []Condition{{
  1136  				Type:   ConditionReconciling,
  1137  				Status: corev1.ConditionTrue,
  1138  				Reason: "LatestGenerationNotObserved",
  1139  			}},
  1140  			absentConditionTypes: []ConditionType{
  1141  				ConditionStalled,
  1142  			},
  1143  		},
  1144  	}
  1145  
  1146  	for tn, tc := range testCases {
  1147  		tc := tc
  1148  		t.Run(tn, func(t *testing.T) {
  1149  			runStatusTest(t, tc)
  1150  		})
  1151  	}
  1152  }
  1153  
  1154  var jobNoStatus = `
  1155  apiVersion: batch/v1
  1156  kind: Job
  1157  metadata:
  1158     name: test
  1159     namespace: qual
  1160     generation: 1
  1161  `
  1162  
  1163  var jobComplete = `
  1164  apiVersion: batch/v1
  1165  kind: Job
  1166  metadata:
  1167     name: test
  1168     namespace: qual
  1169     generation: 1
  1170  status:
  1171     succeeded: 1
  1172     active: 0
  1173     conditions:
  1174      - type: Complete 
  1175        status: "True"
  1176  `
  1177  
  1178  var jobFailed = `
  1179  apiVersion: batch/v1
  1180  kind: Job
  1181  metadata:
  1182     name: test
  1183     namespace: qual
  1184     generation: 1
  1185  spec:
  1186     completions: 4
  1187  status:
  1188     succeeded: 3
  1189     failed: 1
  1190     conditions:
  1191      - type: Failed 
  1192        status: "True"
  1193        reason: JobFailed
  1194  `
  1195  
  1196  var jobInProgress = `
  1197  apiVersion: batch/v1
  1198  kind: Job
  1199  metadata:
  1200     name: test
  1201     namespace: qual
  1202     generation: 1
  1203  spec:
  1204     completions: 10
  1205     parallelism: 2
  1206  status:
  1207     startTime: "2019-06-04T01:17:13Z"
  1208     succeeded: 3
  1209     failed: 1
  1210     active: 2
  1211     conditions:
  1212      - type: Failed 
  1213        status: "False"
  1214      - type: Complete 
  1215        status: "False"
  1216  `
  1217  
  1218  func TestJobStatus(t *testing.T) {
  1219  	testCases := map[string]testSpec{
  1220  		"jobNoStatus": {
  1221  			spec:           jobNoStatus,
  1222  			expectedStatus: InProgressStatus,
  1223  			expectedConditions: []Condition{{
  1224  				Type:   ConditionReconciling,
  1225  				Status: corev1.ConditionTrue,
  1226  				Reason: "JobNotStarted",
  1227  			}},
  1228  			absentConditionTypes: []ConditionType{
  1229  				ConditionStalled,
  1230  			},
  1231  		},
  1232  		"jobComplete": {
  1233  			spec:               jobComplete,
  1234  			expectedStatus:     CurrentStatus,
  1235  			expectedConditions: []Condition{},
  1236  			absentConditionTypes: []ConditionType{
  1237  				ConditionStalled,
  1238  				ConditionReconciling,
  1239  			},
  1240  		},
  1241  		"jobFailed": {
  1242  			spec:           jobFailed,
  1243  			expectedStatus: FailedStatus,
  1244  			expectedConditions: []Condition{{
  1245  				Type:   ConditionStalled,
  1246  				Status: corev1.ConditionTrue,
  1247  				Reason: "JobFailed",
  1248  			}},
  1249  			absentConditionTypes: []ConditionType{
  1250  				ConditionReconciling,
  1251  			},
  1252  		},
  1253  		"jobInProgress": {
  1254  			spec:               jobInProgress,
  1255  			expectedStatus:     CurrentStatus,
  1256  			expectedConditions: []Condition{},
  1257  			absentConditionTypes: []ConditionType{
  1258  				ConditionReconciling,
  1259  				ConditionStalled,
  1260  			},
  1261  		},
  1262  	}
  1263  
  1264  	for tn, tc := range testCases {
  1265  		tc := tc
  1266  		t.Run(tn, func(t *testing.T) {
  1267  			runStatusTest(t, tc)
  1268  		})
  1269  	}
  1270  }
  1271  
  1272  var cronjobNoStatus = `
  1273  apiVersion: batch/v1
  1274  kind: CronJob
  1275  metadata:
  1276     name: test
  1277     namespace: qual
  1278     generation: 1
  1279  `
  1280  
  1281  var cronjobWithStatus = `
  1282  apiVersion: batch/v1
  1283  kind: CronJob
  1284  metadata:
  1285     name: test
  1286     namespace: qual
  1287     generation: 1
  1288  status:
  1289  `
  1290  
  1291  func TestCronJobStatus(t *testing.T) {
  1292  	testCases := map[string]testSpec{
  1293  		"cronjobNoStatus": {
  1294  			spec:               cronjobNoStatus,
  1295  			expectedStatus:     CurrentStatus,
  1296  			expectedConditions: []Condition{},
  1297  			absentConditionTypes: []ConditionType{
  1298  				ConditionStalled,
  1299  				ConditionReconciling,
  1300  			},
  1301  		},
  1302  		"cronjobWithStatus": {
  1303  			spec:               cronjobWithStatus,
  1304  			expectedStatus:     CurrentStatus,
  1305  			expectedConditions: []Condition{},
  1306  			absentConditionTypes: []ConditionType{
  1307  				ConditionStalled,
  1308  				ConditionReconciling,
  1309  			},
  1310  		},
  1311  	}
  1312  
  1313  	for tn, tc := range testCases {
  1314  		tc := tc
  1315  		t.Run(tn, func(t *testing.T) {
  1316  			runStatusTest(t, tc)
  1317  		})
  1318  	}
  1319  }
  1320  
  1321  var serviceDefault = `
  1322  apiVersion: v1
  1323  kind: Service
  1324  metadata:
  1325     name: test
  1326     namespace: qual
  1327     generation: 1
  1328  `
  1329  
  1330  var serviceNodePort = `
  1331  apiVersion: v1
  1332  kind: Service
  1333  metadata:
  1334     name: test
  1335     namespace: qual
  1336     generation: 1
  1337  spec:
  1338    type: NodePort
  1339  `
  1340  
  1341  var serviceLBok = `
  1342  apiVersion: v1
  1343  kind: Service
  1344  metadata:
  1345     name: test
  1346     namespace: qual
  1347     generation: 1
  1348  spec:
  1349    type: LoadBalancer
  1350    clusterIP: "1.2.3.4"
  1351  `
  1352  var serviceLBnok = `
  1353  apiVersion: v1
  1354  kind: Service
  1355  metadata:
  1356     name: test
  1357     namespace: qual
  1358     generation: 1
  1359  spec:
  1360    type: LoadBalancer
  1361  `
  1362  
  1363  func TestServiceStatus(t *testing.T) {
  1364  	testCases := map[string]testSpec{
  1365  		"serviceDefault": {
  1366  			spec:               serviceDefault,
  1367  			expectedStatus:     CurrentStatus,
  1368  			expectedConditions: []Condition{},
  1369  			absentConditionTypes: []ConditionType{
  1370  				ConditionStalled,
  1371  				ConditionReconciling,
  1372  			},
  1373  		},
  1374  		"serviceNodePort": {
  1375  			spec:               serviceNodePort,
  1376  			expectedStatus:     CurrentStatus,
  1377  			expectedConditions: []Condition{},
  1378  			absentConditionTypes: []ConditionType{
  1379  				ConditionStalled,
  1380  				ConditionReconciling,
  1381  			},
  1382  		},
  1383  		"serviceLBnok": {
  1384  			spec:           serviceLBnok,
  1385  			expectedStatus: InProgressStatus,
  1386  			expectedConditions: []Condition{{
  1387  				Type:   ConditionReconciling,
  1388  				Status: corev1.ConditionTrue,
  1389  				Reason: "NoIPAssigned",
  1390  			}},
  1391  			absentConditionTypes: []ConditionType{
  1392  				ConditionStalled,
  1393  			},
  1394  		},
  1395  		"serviceLBok": {
  1396  			spec:               serviceLBok,
  1397  			expectedStatus:     CurrentStatus,
  1398  			expectedConditions: []Condition{},
  1399  			absentConditionTypes: []ConditionType{
  1400  				ConditionStalled,
  1401  				ConditionReconciling,
  1402  			},
  1403  		},
  1404  	}
  1405  
  1406  	for tn, tc := range testCases {
  1407  		tc := tc
  1408  		t.Run(tn, func(t *testing.T) {
  1409  			runStatusTest(t, tc)
  1410  		})
  1411  	}
  1412  }
  1413  
  1414  var crdNoConditions = `
  1415  apiVersion: apiextensions.k8s.io/v1
  1416  kind: CustomResourceDefinition
  1417  metadata:
  1418     generation: 1
  1419  `
  1420  
  1421  var crdInstalling = `
  1422  apiVersion: apiextensions.k8s.io/v1
  1423  kind: CustomResourceDefinition
  1424  metadata:
  1425     generation: 1
  1426  status:
  1427     conditions:
  1428      - type: NamesAccepted
  1429        status: "True"
  1430        reason: NoConflicts
  1431      - type: Established
  1432        status: "False"
  1433        reason: Installing
  1434  `
  1435  
  1436  var crdNamesNotAccepted = `
  1437  apiVersion: apiextensions.k8s.io/v1
  1438  kind: CustomResourceDefinition
  1439  metadata:
  1440     generation: 1
  1441  status:
  1442     conditions:
  1443      - type: NamesAccepted
  1444        status: "False"
  1445        reason: SomeReason
  1446  `
  1447  
  1448  var crdEstablished = `
  1449  apiVersion: apiextensions.k8s.io/v1
  1450  kind: CustomResourceDefinition
  1451  metadata:
  1452     generation: 1
  1453  status:
  1454     conditions:
  1455      - type: NamesAccepted
  1456        status: "True"
  1457        reason: NoConflicts
  1458      - type: Established
  1459        status: "True"
  1460        reason: InitialNamesAccepted
  1461  `
  1462  
  1463  func TestCRDStatus(t *testing.T) {
  1464  	testCases := map[string]testSpec{
  1465  		"crdNoConditions": {
  1466  			spec:           crdNoConditions,
  1467  			expectedStatus: InProgressStatus,
  1468  			expectedConditions: []Condition{
  1469  				{
  1470  					Type:   ConditionReconciling,
  1471  					Status: corev1.ConditionTrue,
  1472  					Reason: "Installing",
  1473  				},
  1474  			},
  1475  			absentConditionTypes: []ConditionType{
  1476  				ConditionStalled,
  1477  			},
  1478  		},
  1479  		"crdInstalling": {
  1480  			spec:           crdInstalling,
  1481  			expectedStatus: InProgressStatus,
  1482  			expectedConditions: []Condition{
  1483  				{
  1484  					Type:   ConditionReconciling,
  1485  					Status: corev1.ConditionTrue,
  1486  					Reason: "Installing",
  1487  				},
  1488  			},
  1489  			absentConditionTypes: []ConditionType{
  1490  				ConditionStalled,
  1491  			},
  1492  		},
  1493  		"crdNamesNotAccepted": {
  1494  			spec:           crdNamesNotAccepted,
  1495  			expectedStatus: FailedStatus,
  1496  			expectedConditions: []Condition{
  1497  				{
  1498  					Type:   ConditionStalled,
  1499  					Status: corev1.ConditionTrue,
  1500  					Reason: "SomeReason",
  1501  				},
  1502  			},
  1503  			absentConditionTypes: []ConditionType{
  1504  				ConditionReconciling,
  1505  			},
  1506  		},
  1507  		"crdEstablished": {
  1508  			spec:               crdEstablished,
  1509  			expectedStatus:     CurrentStatus,
  1510  			expectedConditions: []Condition{},
  1511  			absentConditionTypes: []ConditionType{
  1512  				ConditionReconciling,
  1513  				ConditionStalled,
  1514  			},
  1515  		},
  1516  	}
  1517  
  1518  	for tn, tc := range testCases {
  1519  		tc := tc
  1520  		t.Run(tn, func(t *testing.T) {
  1521  			runStatusTest(t, tc)
  1522  		})
  1523  	}
  1524  }
  1525  

View as plain text