...

Source file src/k8s.io/kubernetes/test/integration/scheduler_perf/create.go

Documentation: k8s.io/kubernetes/test/integration/scheduler_perf

     1  /*
     2  Copyright 2019 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 benchmark
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    25  	"k8s.io/apimachinery/pkg/api/meta"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/client-go/discovery/cached/memory"
    30  	"k8s.io/client-go/restmapper"
    31  	"k8s.io/klog/v2"
    32  	"k8s.io/kubernetes/test/utils/ktesting"
    33  )
    34  
    35  // createAny defines an op where some object gets created from a YAML file.
    36  // The nameset can be specified.
    37  type createAny struct {
    38  	// Must match createAnyOpcode.
    39  	Opcode operationCode
    40  	// Namespace the object should be created in. Must be empty for cluster-scoped objects.
    41  	Namespace string
    42  	// Path to spec file describing the object to create.
    43  	TemplatePath string
    44  }
    45  
    46  var _ runnableOp = &createAny{}
    47  
    48  func (c *createAny) isValid(allowParameterization bool) error {
    49  	if c.Opcode != createAnyOpcode {
    50  		return fmt.Errorf("invalid opcode %q; expected %q", c.Opcode, createAnyOpcode)
    51  	}
    52  	if c.TemplatePath == "" {
    53  		return fmt.Errorf("TemplatePath must be set")
    54  	}
    55  	// The namespace can only be checked during later because we don't know yet
    56  	// whether the object is namespaced or cluster-scoped.
    57  	return nil
    58  }
    59  
    60  func (c *createAny) collectsMetrics() bool {
    61  	return false
    62  }
    63  
    64  func (c *createAny) patchParams(w *workload) (realOp, error) {
    65  	return c, c.isValid(false)
    66  }
    67  
    68  func (c *createAny) requiredNamespaces() []string {
    69  	if c.Namespace == "" {
    70  		return nil
    71  	}
    72  	return []string{c.Namespace}
    73  }
    74  
    75  func (c *createAny) run(tCtx ktesting.TContext) {
    76  	var obj *unstructured.Unstructured
    77  	if err := getSpecFromFile(&c.TemplatePath, &obj); err != nil {
    78  		tCtx.Fatalf("%s: parsing failed: %v", c.TemplatePath, err)
    79  	}
    80  
    81  	// Not caching the discovery result isn't very efficient, but good enough when
    82  	// createAny isn't done often.
    83  	discoveryCache := memory.NewMemCacheClient(tCtx.Client().Discovery())
    84  	restMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryCache)
    85  	gv, err := schema.ParseGroupVersion(obj.GetAPIVersion())
    86  	if err != nil {
    87  		tCtx.Fatalf("%s: extract group+version from object %q: %v", c.TemplatePath, klog.KObj(obj), err)
    88  	}
    89  	gk := schema.GroupKind{Group: gv.Group, Kind: obj.GetKind()}
    90  
    91  	create := func() error {
    92  		mapping, err := restMapper.RESTMapping(gk, gv.Version)
    93  		if err != nil {
    94  			// Cached mapping might be stale, refresh on next try.
    95  			restMapper.Reset()
    96  			return fmt.Errorf("map %q to resource: %v", gk, err)
    97  		}
    98  		resourceClient := tCtx.Dynamic().Resource(mapping.Resource)
    99  		options := metav1.CreateOptions{
   100  			// If the YAML input is invalid, then we want the
   101  			// apiserver to tell us via an error. This can
   102  			// happen because decoding into an unstructured object
   103  			// doesn't validate.
   104  			FieldValidation: "Strict",
   105  		}
   106  		if c.Namespace != "" {
   107  			if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
   108  				return fmt.Errorf("namespace %q set for %q, but %q has scope %q", c.Namespace, c.TemplatePath, gk, mapping.Scope.Name())
   109  			}
   110  			_, err = resourceClient.Namespace(c.Namespace).Create(tCtx, obj, options)
   111  		} else {
   112  			if mapping.Scope.Name() != meta.RESTScopeNameRoot {
   113  				return fmt.Errorf("namespace not set for %q, but %q has scope %q", c.TemplatePath, gk, mapping.Scope.Name())
   114  			}
   115  			_, err = resourceClient.Create(tCtx, obj, options)
   116  		}
   117  		if err == nil && shouldCleanup(tCtx) {
   118  			tCtx.CleanupCtx(func(tCtx ktesting.TContext) {
   119  				del := resourceClient.Delete
   120  				if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
   121  					del = resourceClient.Namespace(c.Namespace).Delete
   122  				}
   123  				err := del(tCtx, obj.GetName(), metav1.DeleteOptions{})
   124  				if !apierrors.IsNotFound(err) {
   125  					tCtx.ExpectNoError(err, fmt.Sprintf("deleting %s.%s %s", obj.GetKind(), obj.GetAPIVersion(), klog.KObj(obj)))
   126  				}
   127  			})
   128  		}
   129  		return err
   130  	}
   131  	// Retry, some errors (like CRD just created and type not ready for use yet) are temporary.
   132  	ctx, cancel := context.WithTimeout(tCtx, 20*time.Second)
   133  	defer cancel()
   134  	for {
   135  		err := create()
   136  		if err == nil {
   137  			return
   138  		}
   139  		select {
   140  		case <-ctx.Done():
   141  			tCtx.Fatalf("%s: timed out (%q) while creating %q, last error was: %v", c.TemplatePath, context.Cause(ctx), klog.KObj(obj), err)
   142  		case <-time.After(time.Second):
   143  		}
   144  	}
   145  }
   146  

View as plain text