k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/measurement/util/wait_for_conditions_test.go (about)

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/runtime/schema"
    28  	"k8s.io/client-go/dynamic/fake"
    29  )
    30  
    31  func TestWaitForGenericK8sObjects(t *testing.T) {
    32  
    33  	tests := []struct {
    34  		name            string
    35  		timeout         time.Duration
    36  		options         *WaitForGenericK8sObjectsOptions
    37  		existingObjects []exampleObject
    38  		wantErr         bool
    39  	}{
    40  		{
    41  			name:    "one successful object",
    42  			timeout: 1 * time.Second,
    43  			options: &WaitForGenericK8sObjectsOptions{
    44  				GroupVersionResource: schema.GroupVersionResource{
    45  					Group:    "kuberentes.io",
    46  					Version:  "v1alpha1",
    47  					Resource: "Conditions",
    48  				},
    49  				Namespaces: NamespacesRange{
    50  					Prefix: "namespace",
    51  					Min:    1,
    52  					Max:    1,
    53  				},
    54  				SuccessfulConditions:  []string{"Successful=True"},
    55  				FailedConditions:      []string{},
    56  				MinDesiredObjectCount: 1,
    57  				MaxFailedObjectCount:  0,
    58  				CallerName:            "test",
    59  				WaitInterval:          100 * time.Millisecond,
    60  			},
    61  			existingObjects: []exampleObject{
    62  				newExampleObject("test-1", "namespace-1", []interface{}{
    63  					map[string]interface{}{
    64  						"type":   "Successful",
    65  						"status": "True",
    66  					},
    67  				}),
    68  			},
    69  		},
    70  		{
    71  			name:    "one failed object",
    72  			timeout: 1 * time.Second,
    73  			options: &WaitForGenericK8sObjectsOptions{
    74  				GroupVersionResource: schema.GroupVersionResource{
    75  					Group:    "kuberentes.io",
    76  					Version:  "v1alpha1",
    77  					Resource: "Conditions",
    78  				},
    79  				Namespaces: NamespacesRange{
    80  					Prefix: "namespace",
    81  					Min:    1,
    82  					Max:    1,
    83  				},
    84  				SuccessfulConditions:  []string{"Successful=True"},
    85  				FailedConditions:      []string{"Failed=True"},
    86  				MinDesiredObjectCount: 1,
    87  				MaxFailedObjectCount:  0,
    88  				CallerName:            "test",
    89  				WaitInterval:          100 * time.Millisecond,
    90  			},
    91  			existingObjects: []exampleObject{
    92  				newExampleObject("test-1", "namespace-1", []interface{}{
    93  					map[string]interface{}{
    94  						"type":   "Successful",
    95  						"status": "False",
    96  					},
    97  					map[string]interface{}{
    98  						"type":   "Failed",
    99  						"status": "True",
   100  					},
   101  				}),
   102  			},
   103  			wantErr: true,
   104  		},
   105  		{
   106  			name:    "one failed object, but one is acceptable",
   107  			timeout: 1 * time.Second,
   108  			options: &WaitForGenericK8sObjectsOptions{
   109  				GroupVersionResource: schema.GroupVersionResource{
   110  					Group:    "kuberentes.io",
   111  					Version:  "v1alpha1",
   112  					Resource: "Conditions",
   113  				},
   114  				Namespaces: NamespacesRange{
   115  					Prefix: "namespace",
   116  					Min:    1,
   117  					Max:    1,
   118  				},
   119  				SuccessfulConditions:  []string{"Successful=True"},
   120  				FailedConditions:      []string{"Failed=True"},
   121  				MinDesiredObjectCount: 3,
   122  				MaxFailedObjectCount:  1,
   123  				CallerName:            "test",
   124  				WaitInterval:          100 * time.Millisecond,
   125  			},
   126  			existingObjects: []exampleObject{
   127  				newExampleObject("test-1", "namespace-1", []interface{}{
   128  					map[string]interface{}{
   129  						"type":   "Successful",
   130  						"status": "False",
   131  					},
   132  					map[string]interface{}{
   133  						"type":   "Failed",
   134  						"status": "True",
   135  					},
   136  				}),
   137  				newExampleObject("test-2", "namespace-1", []interface{}{
   138  					map[string]interface{}{
   139  						"type":   "Successful",
   140  						"status": "True",
   141  					},
   142  				}),
   143  				newExampleObject("test-3", "namespace-1", []interface{}{
   144  					map[string]interface{}{
   145  						"type":   "Successful",
   146  						"status": "True",
   147  					},
   148  				}),
   149  			},
   150  		},
   151  		{
   152  			name:    "timeout due not enough objects",
   153  			timeout: 1 * time.Second,
   154  			options: &WaitForGenericK8sObjectsOptions{
   155  				GroupVersionResource: schema.GroupVersionResource{
   156  					Group:    "kuberentes.io",
   157  					Version:  "v1alpha1",
   158  					Resource: "Conditions",
   159  				},
   160  				Namespaces: NamespacesRange{
   161  					Prefix: "namespace",
   162  					Min:    1,
   163  					Max:    1,
   164  				},
   165  				SuccessfulConditions:  []string{"Successful=True"},
   166  				FailedConditions:      []string{"Failed=True"},
   167  				MinDesiredObjectCount: 3,
   168  				MaxFailedObjectCount:  1,
   169  				CallerName:            "test",
   170  				WaitInterval:          100 * time.Millisecond,
   171  			},
   172  			existingObjects: []exampleObject{
   173  				newExampleObject("test-1", "namespace-1", []interface{}{
   174  					map[string]interface{}{
   175  						"type":   "Successful",
   176  						"status": "False",
   177  					},
   178  					map[string]interface{}{
   179  						"type":   "Failed",
   180  						"status": "True",
   181  					},
   182  				}),
   183  				newExampleObject("test-2", "namespace-1", []interface{}{
   184  					map[string]interface{}{
   185  						"type":   "Successful",
   186  						"status": "True",
   187  					},
   188  				}),
   189  			},
   190  			wantErr: true,
   191  		},
   192  	}
   193  	for _, tt := range tests {
   194  		tt := tt
   195  		t.Run(tt.name, func(t *testing.T) {
   196  			t.Parallel()
   197  			ctx, cancel := context.WithTimeout(context.Background(), tt.timeout)
   198  			defer cancel()
   199  			dynamicClient := fake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), map[schema.GroupVersionResource]string{
   200  				tt.options.GroupVersionResource: "ConditionsList",
   201  			})
   202  			for _, o := range tt.existingObjects {
   203  				c := dynamicClient.Resource(tt.options.GroupVersionResource).Namespace(o.Namespace)
   204  				if _, err := c.Create(ctx, o.Unstructured, metav1.CreateOptions{}); err != nil {
   205  					t.Fatalf("Failed to create an existing object %v, got error: %v", o, err)
   206  				}
   207  			}
   208  
   209  			if err := WaitForGenericK8sObjects(ctx, dynamicClient, tt.options); (err != nil) != tt.wantErr {
   210  				t.Errorf("WaitForGenericK8sObjects() error = %v, wantErr %v", err, tt.wantErr)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  type exampleObject struct {
   217  	Namespace    string
   218  	Unstructured *unstructured.Unstructured
   219  }
   220  
   221  func newExampleObject(name, namespace string, conditions []interface{}) exampleObject {
   222  	return exampleObject{
   223  		Namespace: namespace,
   224  		Unstructured: &unstructured.Unstructured{
   225  			Object: map[string]interface{}{
   226  				"metadata": map[string]interface{}{
   227  					"name":      name,
   228  					"namespace": namespace,
   229  				},
   230  				"status": map[string]interface{}{
   231  					"conditions": conditions,
   232  				},
   233  			},
   234  		},
   235  	}
   236  }