...

Source file src/k8s.io/kubernetes/test/integration/apiserver/discovery/framework.go

Documentation: k8s.io/kubernetes/test/integration/apiserver/discovery

     1  /*
     2  Copyright 2016 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 discovery
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"reflect"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    29  	"k8s.io/apimachinery/pkg/api/errors"
    30  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	"k8s.io/apimachinery/pkg/util/sets"
    34  	"k8s.io/apimachinery/pkg/util/wait"
    35  	"k8s.io/client-go/dynamic"
    36  	"k8s.io/client-go/kubernetes"
    37  	aggregator "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
    38  
    39  	apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
    40  	apidiscoveryv2beta1 "k8s.io/api/apidiscovery/v2beta1"
    41  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    42  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    43  	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
    44  )
    45  
    46  const acceptV1JSON = "application/json"
    47  const acceptV2Beta1JSON = "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
    48  const acceptV2JSON = "application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList"
    49  
    50  const maxTimeout = 10 * time.Second
    51  
    52  type testClient interface {
    53  	kubernetes.Interface
    54  	aggregator.Interface
    55  	apiextensions.Interface
    56  	dynamic.Interface
    57  }
    58  
    59  // declarative framework for discovery integration tests
    60  // each test has metadata and a list of actions which each must pass for the
    61  // test to pass
    62  type testCase struct {
    63  	Name    string
    64  	Actions []testAction
    65  }
    66  
    67  // interface defining a function that does something with the integration test
    68  // api server and returns an error. the test fails if the error is non nil
    69  type testAction interface {
    70  	Do(ctx context.Context, client testClient) error
    71  }
    72  
    73  type cleaningAction interface {
    74  	testAction
    75  	Cleanup(ctx context.Context, client testClient) error
    76  }
    77  
    78  // apply an apiservice to the cluster
    79  type applyAPIService apiregistrationv1.APIServiceSpec
    80  
    81  type applyCRD apiextensionsv1.CustomResourceDefinitionSpec
    82  
    83  type deleteObject struct {
    84  	metav1.GroupVersionResource
    85  	Namespace string
    86  	Name      string
    87  }
    88  
    89  // Wait for groupversions to appear in v1 discovery
    90  type waitForGroupVersionsV1 []metav1.GroupVersion
    91  
    92  // Wait for groupversions to disappear from v2 discovery
    93  type waitForAbsentGroupVersionsV1 []metav1.GroupVersion
    94  
    95  // Wait for groupversions to appear in v2 discovery
    96  type waitForGroupVersionsV2 []metav1.GroupVersion
    97  
    98  // Wait for groupversions to appear in v2beta1 discovery
    99  type waitForGroupVersionsV2Beta1 []metav1.GroupVersion
   100  
   101  // Wait for groupversions to disappear from v2 discovery
   102  type waitForAbsentGroupVersionsV2 []metav1.GroupVersion
   103  
   104  // Wait for groupversions to disappear from v2beta1 discovery
   105  type waitForAbsentGroupVersionsV2Beta1 []metav1.GroupVersion
   106  
   107  type waitForStaleGroupVersionsV2 []metav1.GroupVersion
   108  type waitForFreshGroupVersionsV2 []metav1.GroupVersion
   109  
   110  type waitForResourcesV1 []metav1.GroupVersionResource
   111  type waitForResourcesAbsentV1 []metav1.GroupVersionResource
   112  
   113  type waitForResourcesV2 []metav1.GroupVersionResource
   114  type waitForResourcesAbsentV2 []metav1.GroupVersionResource
   115  
   116  // Assert something about the current state of v2 discovery
   117  type inlineAction func(ctx context.Context, client testClient) error
   118  
   119  func (a applyAPIService) Do(ctx context.Context, client testClient) error {
   120  	// using dynamic client since the typed client does not support `Apply`
   121  	// operation?
   122  	obj := &apiregistrationv1.APIService{
   123  		ObjectMeta: metav1.ObjectMeta{
   124  			Name: a.Version + "." + a.Group,
   125  		},
   126  		Spec: apiregistrationv1.APIServiceSpec(a),
   127  	}
   128  
   129  	unstructuredContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	unstructedObject := &unstructured.Unstructured{}
   135  	unstructedObject.SetUnstructuredContent(unstructuredContent)
   136  	unstructedObject.SetGroupVersionKind(apiregistrationv1.SchemeGroupVersion.WithKind("APIService"))
   137  
   138  	_, err = client.
   139  		Resource(apiregistrationv1.SchemeGroupVersion.WithResource("apiservices")).
   140  		Apply(ctx, obj.Name, unstructedObject, metav1.ApplyOptions{
   141  			FieldManager: "test-manager",
   142  		})
   143  
   144  	return err
   145  }
   146  
   147  func (a applyAPIService) Cleanup(ctx context.Context, client testClient) error {
   148  	name := a.Version + "." + a.Group
   149  	err := client.ApiregistrationV1().APIServices().Delete(ctx, name, metav1.DeleteOptions{})
   150  
   151  	if !errors.IsNotFound(err) {
   152  		return err
   153  	}
   154  
   155  	err = wait.PollUntilContextTimeout(
   156  		ctx,
   157  		250*time.Millisecond,
   158  		maxTimeout,
   159  		true,
   160  		func(ctx context.Context) (done bool, err error) {
   161  			_, err = client.ApiregistrationV1().APIServices().Get(ctx, name, metav1.GetOptions{})
   162  			if err == nil {
   163  				return false, nil
   164  			}
   165  
   166  			if !errors.IsNotFound(err) {
   167  				return false, err
   168  			}
   169  			return true, nil
   170  		},
   171  	)
   172  
   173  	if err != nil {
   174  		return fmt.Errorf("error waiting for APIService %v to clean up: %w", name, err)
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  func (a applyCRD) Do(ctx context.Context, client testClient) error {
   181  	// using dynamic client since the typed client does not support `Apply`
   182  	// operation?
   183  	name := a.Names.Plural + "." + a.Group
   184  	obj := &apiextensionsv1.CustomResourceDefinition{
   185  		ObjectMeta: metav1.ObjectMeta{
   186  			Name: name,
   187  		},
   188  		Spec: apiextensionsv1.CustomResourceDefinitionSpec(a),
   189  	}
   190  
   191  	if strings.HasSuffix(obj.Name, ".k8s.io") {
   192  		if obj.Annotations == nil {
   193  			obj.Annotations = map[string]string{}
   194  		}
   195  		obj.Annotations["api-approved.kubernetes.io"] = "https://github.com/kubernetes/kubernetes/fake"
   196  	}
   197  
   198  	unstructuredContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	unstructedObject := &unstructured.Unstructured{}
   204  	unstructedObject.SetUnstructuredContent(unstructuredContent)
   205  	unstructedObject.SetGroupVersionKind(apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"))
   206  
   207  	_, err = client.
   208  		Resource(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions")).
   209  		Apply(ctx, obj.Name, unstructedObject, metav1.ApplyOptions{
   210  			FieldManager: "test-manager",
   211  		})
   212  
   213  	return err
   214  }
   215  
   216  func (a applyCRD) Cleanup(ctx context.Context, client testClient) error {
   217  	name := a.Names.Plural + "." + a.Group
   218  	err := client.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, name, metav1.DeleteOptions{})
   219  
   220  	if !errors.IsNotFound(err) {
   221  		return err
   222  	}
   223  
   224  	err = wait.PollUntilContextTimeout(
   225  		ctx,
   226  		250*time.Millisecond,
   227  		maxTimeout,
   228  		true,
   229  		func(ctx context.Context) (done bool, err error) {
   230  			_, err = client.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, name, metav1.GetOptions{})
   231  			if err == nil {
   232  				return false, nil
   233  			}
   234  
   235  			if !errors.IsNotFound(err) {
   236  				return false, err
   237  			}
   238  			return true, nil
   239  		},
   240  	)
   241  
   242  	if err != nil {
   243  		return fmt.Errorf("error waiting for CRD %v to clean up: %w", name, err)
   244  	}
   245  
   246  	return nil
   247  }
   248  
   249  func (d deleteObject) Do(ctx context.Context, client testClient) error {
   250  	if d.Namespace == "" {
   251  		return client.Resource(schema.GroupVersionResource(d.GroupVersionResource)).
   252  			Delete(ctx, d.Name, metav1.DeleteOptions{})
   253  	} else {
   254  		return client.Resource(schema.GroupVersionResource(d.GroupVersionResource)).
   255  			Namespace(d.Namespace).
   256  			Delete(ctx, d.Name, metav1.DeleteOptions{})
   257  	}
   258  }
   259  
   260  func (w waitForStaleGroupVersionsV2) Do(ctx context.Context, client testClient) error {
   261  	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
   262  		for _, gv := range w {
   263  			if info := FindGroupVersionV2(result, gv); info == nil || info.Freshness != apidiscoveryv2.DiscoveryFreshnessStale {
   264  				return false
   265  			}
   266  		}
   267  
   268  		return true
   269  	})
   270  
   271  	if err != nil {
   272  		return fmt.Errorf("waiting for stale groupversions v2 (%v): %w", w, err)
   273  	}
   274  	return nil
   275  }
   276  
   277  func (w waitForFreshGroupVersionsV2) Do(ctx context.Context, client testClient) error {
   278  	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
   279  		for _, gv := range w {
   280  			if info := FindGroupVersionV2(result, gv); info == nil || info.Freshness != apidiscoveryv2.DiscoveryFreshnessCurrent {
   281  				return false
   282  			}
   283  		}
   284  
   285  		return true
   286  	})
   287  
   288  	if err != nil {
   289  		return fmt.Errorf("waiting for fresh groupversions v2 (%v): %w", w, err)
   290  	}
   291  	return nil
   292  }
   293  
   294  func (w waitForGroupVersionsV2) Do(ctx context.Context, client testClient) error {
   295  	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
   296  		for _, gv := range w {
   297  			if FindGroupVersionV2(result, gv) == nil {
   298  				return false
   299  			}
   300  		}
   301  
   302  		return true
   303  	})
   304  
   305  	if err != nil {
   306  		return fmt.Errorf("waiting for groupversions v2 (%v): %w", w, err)
   307  	}
   308  	return nil
   309  }
   310  
   311  func (w waitForGroupVersionsV2Beta1) Do(ctx context.Context, client testClient) error {
   312  	err := WaitForV2Beta1ResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
   313  		for _, gv := range w {
   314  			if FindGroupVersionV2Beta1(result, gv) == nil {
   315  				return false
   316  			}
   317  		}
   318  
   319  		return true
   320  	})
   321  
   322  	if err != nil {
   323  		return fmt.Errorf("waiting for groupversions v2 (%v): %w", w, err)
   324  	}
   325  	return nil
   326  }
   327  
   328  func (w waitForAbsentGroupVersionsV2) Do(ctx context.Context, client testClient) error {
   329  	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
   330  		for _, gv := range w {
   331  			if FindGroupVersionV2(result, gv) != nil {
   332  				return false
   333  			}
   334  		}
   335  
   336  		return true
   337  	})
   338  
   339  	if err != nil {
   340  		return fmt.Errorf("waiting for absent groupversions v2 (%v): %w", w, err)
   341  	}
   342  	return nil
   343  }
   344  
   345  func (w waitForAbsentGroupVersionsV2Beta1) Do(ctx context.Context, client testClient) error {
   346  	err := WaitForV2Beta1ResultWithCondition(ctx, client, func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool {
   347  		for _, gv := range w {
   348  			if FindGroupVersionV2Beta1(result, gv) != nil {
   349  				return false
   350  			}
   351  		}
   352  
   353  		return true
   354  	})
   355  
   356  	if err != nil {
   357  		return fmt.Errorf("waiting for absent groupversions v2 (%v): %w", w, err)
   358  	}
   359  	return nil
   360  }
   361  
   362  func (w waitForGroupVersionsV1) Do(ctx context.Context, client testClient) error {
   363  	err := WaitForV1GroupsWithCondition(ctx, client, func(result metav1.APIGroupList) bool {
   364  		for _, gv := range w {
   365  			if !FindGroupVersionV1(result, gv) {
   366  				return false
   367  			}
   368  		}
   369  
   370  		return true
   371  	})
   372  
   373  	if err != nil {
   374  		return fmt.Errorf("waiting for groupversions v1 (%v): %w", w, err)
   375  	}
   376  	return nil
   377  }
   378  
   379  func (w waitForAbsentGroupVersionsV1) Do(ctx context.Context, client testClient) error {
   380  	err := WaitForV1GroupsWithCondition(ctx, client, func(result metav1.APIGroupList) bool {
   381  		for _, gv := range w {
   382  			if FindGroupVersionV1(result, gv) {
   383  				return false
   384  			}
   385  		}
   386  
   387  		return true
   388  	})
   389  
   390  	if err != nil {
   391  		return fmt.Errorf("waiting for absent groupversions v1 (%v): %w", w, err)
   392  	}
   393  	return nil
   394  }
   395  
   396  func (w waitForResourcesV1) Do(ctx context.Context, client testClient) error {
   397  	requiredResources := map[metav1.GroupVersion][]string{}
   398  
   399  	for _, gvr := range w {
   400  		gv := metav1.GroupVersion{Group: gvr.Group, Version: gvr.Version}
   401  		if existing, ok := requiredResources[gv]; ok {
   402  			requiredResources[gv] = append(existing, gvr.Resource)
   403  		} else {
   404  			requiredResources[gv] = []string{gvr.Resource}
   405  		}
   406  	}
   407  
   408  	for gv, resourceNames := range requiredResources {
   409  		err := WaitForV1ResourcesWithCondition(ctx, client, gv, func(result metav1.APIResourceList) bool {
   410  			for _, name := range resourceNames {
   411  				found := false
   412  
   413  				for _, resultResource := range result.APIResources {
   414  					if resultResource.Name == name {
   415  						found = true
   416  						break
   417  					}
   418  				}
   419  
   420  				if !found {
   421  					return false
   422  				}
   423  			}
   424  
   425  			return true
   426  		})
   427  
   428  		if err != nil {
   429  			if errors.IsNotFound(err) {
   430  				return nil
   431  			}
   432  			return fmt.Errorf("waiting for resources v1 (%v): %w", w, err)
   433  		}
   434  	}
   435  
   436  	return nil
   437  }
   438  
   439  func (w waitForResourcesAbsentV1) Do(ctx context.Context, client testClient) error {
   440  	requiredResources := map[metav1.GroupVersion][]string{}
   441  
   442  	for _, gvr := range w {
   443  		gv := metav1.GroupVersion{Group: gvr.Group, Version: gvr.Version}
   444  		if existing, ok := requiredResources[gv]; ok {
   445  			requiredResources[gv] = append(existing, gvr.Resource)
   446  		} else {
   447  			requiredResources[gv] = []string{gvr.Resource}
   448  		}
   449  	}
   450  
   451  	for gv, resourceNames := range requiredResources {
   452  		err := WaitForV1ResourcesWithCondition(ctx, client, gv, func(result metav1.APIResourceList) bool {
   453  			for _, name := range resourceNames {
   454  				for _, resultResource := range result.APIResources {
   455  					if resultResource.Name == name {
   456  						return false
   457  					}
   458  				}
   459  			}
   460  
   461  			return true
   462  		})
   463  
   464  		if err != nil {
   465  			if errors.IsNotFound(err) {
   466  				return nil
   467  			}
   468  			return fmt.Errorf("waiting for absent resources v1 (%v): %w", w, err)
   469  		}
   470  	}
   471  
   472  	return nil
   473  }
   474  
   475  func (w waitForResourcesV2) Do(ctx context.Context, client testClient) error {
   476  	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
   477  		for _, gvr := range w {
   478  			if info := FindGroupVersionV2(result, metav1.GroupVersion{Group: gvr.Group, Version: gvr.Version}); info == nil {
   479  				return false
   480  			} else {
   481  				found := false
   482  				for _, resultResoure := range info.Resources {
   483  					if resultResoure.Resource == gvr.Resource {
   484  						found = true
   485  						break
   486  					}
   487  				}
   488  
   489  				if !found {
   490  					return false
   491  				}
   492  			}
   493  		}
   494  
   495  		return true
   496  	})
   497  
   498  	if err != nil {
   499  		return fmt.Errorf("waiting for resources v2 (%v): %w", w, err)
   500  	}
   501  	return nil
   502  }
   503  
   504  func (w waitForResourcesAbsentV2) Do(ctx context.Context, client testClient) error {
   505  	err := WaitForResultWithCondition(ctx, client, func(result apidiscoveryv2.APIGroupDiscoveryList) bool {
   506  		for _, gvr := range w {
   507  			if info := FindGroupVersionV2(result, metav1.GroupVersion{Group: gvr.Group, Version: gvr.Version}); info == nil {
   508  				return false
   509  			} else {
   510  				for _, resultResoure := range info.Resources {
   511  					if resultResoure.Resource == gvr.Resource {
   512  						return false
   513  					}
   514  				}
   515  			}
   516  		}
   517  
   518  		return true
   519  	})
   520  
   521  	if err != nil {
   522  		return fmt.Errorf("waiting for absent resources v2 (%v): %w", w, err)
   523  	}
   524  	return nil
   525  }
   526  
   527  func (i inlineAction) Do(ctx context.Context, client testClient) error {
   528  	return i(ctx, client)
   529  }
   530  
   531  func FetchV2Discovery(ctx context.Context, client testClient) (apidiscoveryv2.APIGroupDiscoveryList, error) {
   532  	result, err := client.
   533  		Discovery().
   534  		RESTClient().
   535  		Get().
   536  		AbsPath("/apis").
   537  		SetHeader("Accept", acceptV2JSON).
   538  		Do(ctx).
   539  		Raw()
   540  
   541  	if err != nil {
   542  		return apidiscoveryv2.APIGroupDiscoveryList{}, fmt.Errorf("failed to fetch v2 discovery: %w", err)
   543  	}
   544  
   545  	groupList := apidiscoveryv2.APIGroupDiscoveryList{}
   546  	err = json.Unmarshal(result, &groupList)
   547  	if err != nil {
   548  		return apidiscoveryv2.APIGroupDiscoveryList{}, fmt.Errorf("failed to parse v2 discovery: %w", err)
   549  	}
   550  
   551  	return groupList, nil
   552  }
   553  
   554  func FetchV2Beta1Discovery(ctx context.Context, client testClient) (apidiscoveryv2beta1.APIGroupDiscoveryList, error) {
   555  	result, err := client.
   556  		Discovery().
   557  		RESTClient().
   558  		Get().
   559  		AbsPath("/apis").
   560  		SetHeader("Accept", acceptV2Beta1JSON).
   561  		Do(ctx).
   562  		Raw()
   563  
   564  	if err != nil {
   565  		return apidiscoveryv2beta1.APIGroupDiscoveryList{}, fmt.Errorf("failed to fetch v2 discovery: %w", err)
   566  	}
   567  
   568  	groupList := apidiscoveryv2beta1.APIGroupDiscoveryList{}
   569  	err = json.Unmarshal(result, &groupList)
   570  	if err != nil {
   571  		return apidiscoveryv2beta1.APIGroupDiscoveryList{}, fmt.Errorf("failed to parse v2 discovery: %w", err)
   572  	}
   573  
   574  	return groupList, nil
   575  }
   576  
   577  func FetchV1DiscoveryGroups(ctx context.Context, client testClient) (metav1.APIGroupList, error) {
   578  	return FetchV1DiscoveryGroupsAtPath(ctx, client, "/apis")
   579  }
   580  
   581  func FetchV1DiscoveryLegacyGroups(ctx context.Context, client testClient) (metav1.APIGroupList, error) {
   582  	return FetchV1DiscoveryGroupsAtPath(ctx, client, "/api")
   583  }
   584  
   585  func FetchV1DiscoveryGroupsAtPath(ctx context.Context, client testClient, path string) (metav1.APIGroupList, error) {
   586  	result, err := client.
   587  		Discovery().
   588  		RESTClient().
   589  		Get().
   590  		AbsPath(path).
   591  		SetHeader("Accept", acceptV1JSON).
   592  		Do(ctx).
   593  		Raw()
   594  
   595  	if err != nil {
   596  		return metav1.APIGroupList{}, fmt.Errorf("failed to fetch v1 discovery at %v: %w", path, err)
   597  	}
   598  
   599  	groupList := metav1.APIGroupList{}
   600  	err = json.Unmarshal(result, &groupList)
   601  	if err != nil {
   602  		return metav1.APIGroupList{}, fmt.Errorf("failed to parse v1 discovery at %v: %w", path, err)
   603  	}
   604  
   605  	return groupList, nil
   606  }
   607  
   608  func FetchV1DiscoveryResource(ctx context.Context, client testClient, gv metav1.GroupVersion) (metav1.APIResourceList, error) {
   609  	result, err := client.
   610  		Discovery().
   611  		RESTClient().
   612  		Get().
   613  		AbsPath("/apis/"+gv.Group+"/"+gv.Version).
   614  		SetHeader("Accept", acceptV1JSON).
   615  		Do(ctx).
   616  		Raw()
   617  
   618  	if err != nil {
   619  		return metav1.APIResourceList{}, err
   620  	}
   621  
   622  	groupList := metav1.APIResourceList{}
   623  	err = json.Unmarshal(result, &groupList)
   624  	if err != nil {
   625  		return metav1.APIResourceList{}, err
   626  	}
   627  
   628  	return groupList, nil
   629  }
   630  
   631  func WaitForGroupsAbsent(ctx context.Context, client testClient, groups ...string) error {
   632  	return WaitForResultWithCondition(ctx, client, func(groupList apidiscoveryv2.APIGroupDiscoveryList) bool {
   633  		for _, searchGroup := range groups {
   634  			for _, docGroup := range groupList.Items {
   635  				if docGroup.Name == searchGroup {
   636  					return false
   637  				}
   638  			}
   639  		}
   640  		return true
   641  	})
   642  
   643  }
   644  
   645  func WaitForRootPaths(t *testing.T, ctx context.Context, client testClient, requirePaths, forbidPaths sets.Set[string]) error {
   646  	return wait.PollUntilContextTimeout(ctx, 250*time.Millisecond, maxTimeout, true, func(ctx context.Context) (done bool, err error) {
   647  		statusContent, err := client.Discovery().RESTClient().Get().AbsPath("/").SetHeader("Accept", "application/json").DoRaw(ctx)
   648  		if err != nil {
   649  			return false, err
   650  		}
   651  		rootPaths := metav1.RootPaths{}
   652  		if err := json.Unmarshal(statusContent, &rootPaths); err != nil {
   653  			return false, err
   654  		}
   655  		paths := sets.New(rootPaths.Paths...)
   656  		if missing := requirePaths.Difference(paths); len(missing) > 0 {
   657  			t.Logf("missing required root paths %v", sets.List(missing))
   658  			return false, nil
   659  		}
   660  		if present := forbidPaths.Intersection(paths); len(present) > 0 {
   661  			t.Logf("present forbidden root paths %v", sets.List(present))
   662  			return false, nil
   663  		}
   664  		return true, nil
   665  	})
   666  }
   667  
   668  func WaitForGroups(ctx context.Context, client testClient, groups ...apidiscoveryv2.APIGroupDiscovery) error {
   669  	return WaitForResultWithCondition(ctx, client, func(groupList apidiscoveryv2.APIGroupDiscoveryList) bool {
   670  		for _, searchGroup := range groups {
   671  			found := false
   672  			for _, docGroup := range groupList.Items {
   673  				if reflect.DeepEqual(searchGroup, docGroup) {
   674  					found = true
   675  					break
   676  				}
   677  			}
   678  			if !found {
   679  				return false
   680  			}
   681  		}
   682  		return true
   683  	})
   684  }
   685  
   686  func WaitForResultWithCondition(ctx context.Context, client testClient, condition func(result apidiscoveryv2.APIGroupDiscoveryList) bool) error {
   687  	// Keep repeatedly fetching document from aggregator.
   688  	// Check to see if it contains our service within a reasonable amount of time
   689  	return wait.PollUntilContextTimeout(
   690  		ctx,
   691  		250*time.Millisecond,
   692  		maxTimeout,
   693  		true,
   694  		func(ctx context.Context) (done bool, err error) {
   695  			groupList, err := FetchV2Discovery(ctx, client)
   696  			if err != nil {
   697  				return false, err
   698  			}
   699  
   700  			if condition(groupList) {
   701  				return true, nil
   702  			}
   703  
   704  			return false, nil
   705  		})
   706  }
   707  
   708  func WaitForV2Beta1ResultWithCondition(ctx context.Context, client testClient, condition func(result apidiscoveryv2beta1.APIGroupDiscoveryList) bool) error {
   709  	// Keep repeatedly fetching document from aggregator.
   710  	// Check to see if it contains our service within a reasonable amount of time
   711  	return wait.PollUntilContextTimeout(
   712  		ctx,
   713  		250*time.Millisecond,
   714  		maxTimeout,
   715  		true,
   716  		func(ctx context.Context) (done bool, err error) {
   717  			groupList, err := FetchV2Beta1Discovery(ctx, client)
   718  			if err != nil {
   719  				return false, err
   720  			}
   721  
   722  			if condition(groupList) {
   723  				return true, nil
   724  			}
   725  
   726  			return false, nil
   727  		})
   728  }
   729  
   730  func WaitForV1GroupsWithCondition(ctx context.Context, client testClient, condition func(result metav1.APIGroupList) bool) error {
   731  	// Keep repeatedly fetching document from aggregator.
   732  	// Check to see if it contains our service within a reasonable amount of time
   733  	return wait.PollUntilContextTimeout(
   734  		ctx,
   735  		250*time.Millisecond,
   736  		maxTimeout,
   737  		true,
   738  		func(ctx context.Context) (done bool, err error) {
   739  			groupList, err := FetchV1DiscoveryGroups(ctx, client)
   740  
   741  			if err != nil {
   742  				return false, err
   743  			}
   744  
   745  			if condition(groupList) {
   746  				return true, nil
   747  			}
   748  
   749  			return false, nil
   750  		})
   751  }
   752  
   753  func WaitForV1ResourcesWithCondition(ctx context.Context, client testClient, gv metav1.GroupVersion, condition func(result metav1.APIResourceList) bool) error {
   754  	// Keep repeatedly fetching document from aggregator.
   755  	// Check to see if it contains our service within a reasonable amount of time
   756  	return wait.PollUntilContextTimeout(
   757  		ctx,
   758  		250*time.Millisecond,
   759  		maxTimeout,
   760  		true,
   761  		func(ctx context.Context) (done bool, err error) {
   762  			resourceList, err := FetchV1DiscoveryResource(ctx, client, gv)
   763  
   764  			if err != nil {
   765  				return false, err
   766  			}
   767  
   768  			if condition(resourceList) {
   769  				return true, nil
   770  			}
   771  
   772  			return false, nil
   773  		})
   774  }
   775  
   776  func FindGroupVersionV1(discovery metav1.APIGroupList, gv metav1.GroupVersion) bool {
   777  	for _, documentGroup := range discovery.Groups {
   778  		if documentGroup.Name != gv.Group {
   779  			continue
   780  		}
   781  
   782  		for _, documentVersion := range documentGroup.Versions {
   783  			if documentVersion.Version == gv.Version {
   784  				return true
   785  			}
   786  		}
   787  	}
   788  
   789  	return false
   790  }
   791  
   792  func FindGroupVersionV2(discovery apidiscoveryv2.APIGroupDiscoveryList, gv metav1.GroupVersion) *apidiscoveryv2.APIVersionDiscovery {
   793  	for _, documentGroup := range discovery.Items {
   794  		if documentGroup.Name != gv.Group {
   795  			continue
   796  		}
   797  
   798  		for _, documentVersion := range documentGroup.Versions {
   799  			if documentVersion.Version == gv.Version {
   800  				return &documentVersion
   801  			}
   802  		}
   803  	}
   804  
   805  	return nil
   806  }
   807  
   808  func FindGroupVersionV2Beta1(discovery apidiscoveryv2beta1.APIGroupDiscoveryList, gv metav1.GroupVersion) *apidiscoveryv2beta1.APIVersionDiscovery {
   809  	for _, documentGroup := range discovery.Items {
   810  		if documentGroup.Name != gv.Group {
   811  			continue
   812  		}
   813  
   814  		for _, documentVersion := range documentGroup.Versions {
   815  			if documentVersion.Version == gv.Version {
   816  				return &documentVersion
   817  			}
   818  		}
   819  	}
   820  
   821  	return nil
   822  }
   823  

View as plain text