github.com/nginxinc/kubernetes-ingress@v1.12.5/tests/suite/custom_resources_utils.py (about)

     1  """Describe methods to utilize the kubernetes-client."""
     2  import pytest
     3  import time
     4  import yaml
     5  import logging
     6  
     7  from pprint import pprint
     8  from kubernetes.client import CustomObjectsApi, ApiextensionsV1Api, CoreV1Api
     9  from kubernetes import client
    10  from kubernetes.client.rest import ApiException
    11  
    12  from suite.resources_utils import ensure_item_removal, get_file_contents
    13  
    14  
    15  def create_crd(api_extensions_v1: ApiextensionsV1Api, body) -> None:
    16      """
    17      Create a CRD based on a dict
    18  
    19      :param api_extensions_v1: ApiextensionsV1Api
    20      :param body: a dict
    21      """
    22      try:
    23          api_extensions_v1.create_custom_resource_definition(body)
    24      except ApiException as api_ex:
    25          raise api_ex
    26      except Exception as ex:
    27          # https://github.com/kubernetes-client/python/issues/376
    28          if ex.args[0] == "Invalid value for `conditions`, must not be `None`":
    29              print("There was an insignificant exception during the CRD creation. Continue...")
    30          else:
    31              pytest.fail(f"An unexpected exception {ex} occurred. Exiting...")
    32  
    33  
    34  def create_crd_from_yaml(
    35      api_extensions_v1: ApiextensionsV1Api, name, yaml_manifest
    36  ) -> None:
    37      """
    38      Create a specific CRD based on yaml file.
    39  
    40      :param api_extensions_v1: ApiextensionsV1Api
    41      :param name: CRD name
    42      :param yaml_manifest: an absolute path to file
    43      """
    44      print(f"Create a CRD with name: {name}")
    45      with open(yaml_manifest) as f:
    46          docs = yaml.safe_load_all(f)
    47          for dep in docs:
    48              if dep["metadata"]["name"] == name:
    49                  create_crd(api_extensions_v1, dep)
    50                  print("CRD was created")
    51  
    52  
    53  def delete_crd(api_extensions_v1: ApiextensionsV1Api, name) -> None:
    54      """
    55      Delete a CRD.
    56  
    57      :param api_extensions_v1: ApiextensionsV1Api
    58      :param name:
    59      :return:
    60      """
    61      print(f"Delete a CRD: {name}")
    62      api_extensions_v1.delete_custom_resource_definition(name)
    63      ensure_item_removal(api_extensions_v1.read_custom_resource_definition, name)
    64      print(f"CRD was removed with name '{name}'")
    65  
    66  
    67  def read_custom_resource(custom_objects: CustomObjectsApi, namespace, plural, name) -> object:
    68      """
    69      Get CRD information (kubectl describe output)
    70  
    71      :param custom_objects: CustomObjectsApi
    72      :param namespace: The custom resource's namespace	
    73      :param plural: the custom resource's plural name
    74      :param name: the custom object's name
    75      :return: object
    76      """
    77      print(f"Getting info for {name} in namespace {namespace}")
    78      try:
    79          response = custom_objects.get_namespaced_custom_object(
    80              "k8s.nginx.org", "v1", namespace, plural, name
    81          )
    82          pprint(response)
    83          return response
    84  
    85      except ApiException:
    86          logging.exception(f"Exception occurred while reading CRD")
    87          raise
    88  
    89  
    90  def read_custom_resource_v1alpha1(custom_objects: CustomObjectsApi, namespace, plural, name) -> object:
    91      """
    92      Get CRD information (kubectl describe output)
    93  
    94      :param custom_objects: CustomObjectsApi
    95      :param namespace: The custom resource's namespace
    96      :param plural: the custom resource's plural name
    97      :param name: the custom object's name
    98      :return: object
    99      """
   100      print(f"Getting info for v1alpha1 crd {name} in namespace {namespace}")
   101      try:
   102          response = custom_objects.get_namespaced_custom_object(
   103              "k8s.nginx.org", "v1alpha1", namespace, plural, name
   104          )
   105          pprint(response)
   106          return response
   107  
   108      except ApiException:
   109          logging.exception(f"Exception occurred while reading CRD")
   110          raise
   111  
   112  
   113  def read_ts(custom_objects: CustomObjectsApi, namespace, name) -> object:
   114      """
   115      Read TransportService resource.
   116      """
   117      return read_custom_resource_v1alpha1(custom_objects, namespace, "transportservers", name)
   118  
   119  def read_vs(custom_objects: CustomObjectsApi, namespace, name) -> object:
   120      """
   121      Read VirtualServer resource.
   122      """
   123      return read_custom_resource(custom_objects, namespace, "virtualservers", name)
   124  
   125  def read_vsr(custom_objects: CustomObjectsApi, namespace, name) -> object:
   126      """
   127      Read VirtualServerRoute resource.
   128      """
   129      return read_custom_resource(custom_objects, namespace, "virtualserverroutes", name)
   130  
   131  def read_policy(custom_objects: CustomObjectsApi, namespace, name) -> object:
   132      """
   133      Read Policy resource.
   134      """
   135      return read_custom_resource(custom_objects, namespace, "policies", name)
   136  
   137  def read_ap_custom_resource(custom_objects: CustomObjectsApi, namespace, plural, name) -> object:
   138      """
   139      Get AppProtect CRD information (kubectl describe output)
   140      :param custom_objects: CustomObjectsApi
   141      :param namespace: The custom resource's namespace	
   142      :param plural: the custom resource's plural name
   143      :param name: the custom object's name
   144      :return: object
   145      """
   146      print(f"Getting info for {name} in namespace {namespace}")
   147      try:
   148          response = custom_objects.get_namespaced_custom_object(
   149              "appprotect.f5.com", "v1beta1", namespace, plural, name
   150          )
   151          return response
   152  
   153      except ApiException:
   154          logging.exception(f"Exception occurred while reading CRD")
   155          raise
   156  
   157  
   158  def create_policy_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> str:
   159      """
   160      Create a Policy based on yaml file.
   161  
   162      :param custom_objects: CustomObjectsApi
   163      :param yaml_manifest: an absolute path to file
   164      :param namespace:
   165      :return: str
   166      """
   167      print("Create a Policy:")
   168      with open(yaml_manifest) as f:
   169          dep = yaml.safe_load(f)
   170      try:
   171          custom_objects.create_namespaced_custom_object(
   172              "k8s.nginx.org", "v1", namespace, "policies", dep
   173          )
   174          print(f"Policy created with name '{dep['metadata']['name']}'")
   175          return dep["metadata"]["name"]
   176      except ApiException:
   177          logging.exception(f"Exception occurred while creating Policy: {dep['metadata']['name']}")
   178          raise
   179  
   180  
   181  def create_ap_waf_policy_from_yaml(
   182      custom_objects: CustomObjectsApi,
   183      yaml_manifest,
   184      namespace,
   185      ap_namespace,
   186      waf_enable,
   187      log_enable,
   188      appolicy,
   189      aplogconf,
   190      logdest,
   191  ) -> None:
   192      """
   193      Create a Policy based on yaml file.
   194  
   195      :param custom_objects: CustomObjectsApi
   196      :param yaml_manifest: an absolute path to file
   197      :param namespace: namespace for test resources
   198      :param ap_namespace: namespace for AppProtect resources
   199      :param waf_enable: true/false
   200      :param log_enable: true/false
   201      :param appolicy: AppProtect policy name
   202      :param aplogconf: Logconf name
   203      :param logdest: AP log destination (syslog)
   204      :return: None
   205      """
   206      with open(yaml_manifest) as f:
   207          dep = yaml.safe_load(f)
   208      try:
   209          dep["spec"]["waf"]["enable"] = waf_enable
   210          dep["spec"]["waf"]["apPolicy"] = f"{ap_namespace}/{appolicy}"
   211          dep["spec"]["waf"]["securityLog"]["enable"] = log_enable
   212          dep["spec"]["waf"]["securityLog"]["apLogConf"] = f"{ap_namespace}/{aplogconf}"
   213          dep["spec"]["waf"]["securityLog"]["logDest"] = f"{logdest}"
   214  
   215          custom_objects.create_namespaced_custom_object(
   216              "k8s.nginx.org", "v1", namespace, "policies", dep
   217          )
   218          print(f"Policy created: {dep}")
   219      except ApiException:
   220          logging.exception(f"Exception occurred while creating Policy: {dep['metadata']['name']}")
   221          raise
   222  
   223  
   224  def delete_policy(custom_objects: CustomObjectsApi, name, namespace) -> None:
   225      """
   226      Delete a Policy.
   227  
   228      :param custom_objects: CustomObjectsApi
   229      :param namespace: namespace
   230      :param name:
   231      :return:
   232      """
   233      print(f"Delete a Policy: {name}")
   234  
   235      custom_objects.delete_namespaced_custom_object(
   236          "k8s.nginx.org", "v1", namespace, "policies", name
   237      )
   238      ensure_item_removal(
   239          custom_objects.get_namespaced_custom_object,
   240          "k8s.nginx.org",
   241          "v1",
   242          namespace,
   243          "policies",
   244          name,
   245      )
   246      print(f"Policy was removed with name '{name}'")
   247  
   248  
   249  def create_virtual_server_from_yaml(
   250      custom_objects: CustomObjectsApi, yaml_manifest, namespace
   251  ) -> str:
   252      """
   253      Create a VirtualServer based on yaml file.
   254  
   255      :param custom_objects: CustomObjectsApi
   256      :param yaml_manifest: an absolute path to file
   257      :param namespace:
   258      :return: str
   259      """
   260      print("Create a VirtualServer:")
   261      with open(yaml_manifest) as f:
   262          dep = yaml.safe_load(f)
   263      try:
   264          custom_objects.create_namespaced_custom_object(
   265              "k8s.nginx.org", "v1", namespace, "virtualservers", dep
   266          )
   267          print(f"VirtualServer created with name '{dep['metadata']['name']}'")
   268          return dep["metadata"]["name"]
   269      except ApiException as ex:
   270          logging.exception(
   271              f"Exception: {ex} occurred while creating VirtualServer: {dep['metadata']['name']}"
   272          )
   273          raise
   274  
   275  
   276  def create_ts_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> dict:
   277      """
   278      Create a TransportServer Resource based on yaml file.
   279  
   280      :param custom_objects: CustomObjectsApi
   281      :param yaml_manifest: an absolute path to file
   282      :param namespace:
   283      :return: a dictionary representing the resource
   284      """
   285      return create_resource_from_yaml(custom_objects, yaml_manifest, namespace, "transportservers")
   286  
   287  
   288  def create_gc_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> dict:
   289      """
   290      Create a GlobalConfiguration Resource based on yaml file.
   291  
   292      :param custom_objects: CustomObjectsApi
   293      :param yaml_manifest: an absolute path to file
   294      :param namespace:
   295      :return: a dictionary representing the resource
   296      """
   297      return create_resource_from_yaml(custom_objects, yaml_manifest, namespace, "globalconfigurations")
   298  
   299  
   300  def create_resource_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace, plural) -> dict:
   301      """
   302      Create a Resource based on yaml file.
   303  
   304      :param custom_objects: CustomObjectsApi
   305      :param yaml_manifest: an absolute path to file
   306      :param namespace:
   307      :param plural: the plural of the resource
   308      :return: a dictionary representing the resource
   309      """
   310  
   311      with open(yaml_manifest) as f:
   312          body = yaml.safe_load(f)
   313      try:
   314          print("Create a Custom Resource: " + body["kind"])
   315          group, version = body["apiVersion"].split("/")
   316          custom_objects.create_namespaced_custom_object(
   317               group, version, namespace, plural, body
   318          )
   319          print(f"Custom resource {body['kind']} created with name '{body['metadata']['name']}'")
   320          return body
   321      except ApiException as ex:
   322          logging.exception(
   323              f"Exception: {ex} occurred while creating {body['kind']}: {body['metadata']['name']}"
   324          )
   325          raise
   326  
   327  
   328  def delete_ts(custom_objects: CustomObjectsApi, resource, namespace) -> None:
   329      """
   330      Delete a TransportServer Resource.
   331  
   332      :param custom_objects: CustomObjectsApi
   333      :param namespace: namespace
   334      :param resource: a dictionary representation of the resource yaml
   335      :param namespace:
   336      :return:
   337      """
   338      return delete_resource(custom_objects, resource, namespace, "transportservers")
   339  
   340  
   341  def delete_gc(custom_objects: CustomObjectsApi, resource, namespace) -> None:
   342      """
   343      Delete a GlobalConfiguration Resource.
   344  
   345      :param custom_objects: CustomObjectsApi
   346      :param namespace: namespace
   347      :param resource: a dictionary representation of the resource yaml
   348      :param namespace:
   349      :return:
   350      """
   351      return delete_resource(custom_objects, resource, namespace, "globalconfigurations")
   352  
   353  
   354  def delete_resource(custom_objects: CustomObjectsApi, resource, namespace, plural) -> None:
   355      """
   356      Delete a Resource.
   357  
   358      :param custom_objects: CustomObjectsApi
   359      :param namespace: namespace
   360      :param resource: a dictionary representation of the resource yaml
   361      :param namespace:
   362      :param plural: the plural of the resource
   363      :return:
   364      """
   365  
   366      name = resource['metadata']['name']
   367      kind = resource['kind']
   368      group, version = resource["apiVersion"].split("/")
   369  
   370      print(f"Delete a '{kind}' with name '{name}'")
   371  
   372      custom_objects.delete_namespaced_custom_object(
   373          group, version, namespace, plural, name
   374      )
   375      ensure_item_removal(
   376          custom_objects.get_namespaced_custom_object,
   377          group,
   378          version,
   379          namespace,
   380          plural,
   381          name,
   382      )
   383      print(f"Resource '{kind}' was removed with name '{name}'")
   384  
   385  
   386  def create_ap_logconf_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> str:
   387      """
   388      Create a logconf for AppProtect based on yaml file.
   389      :param custom_objects: CustomObjectsApi
   390      :param yaml_manifest: an absolute path to file
   391      :param namespace:
   392      :return: str
   393      """
   394      print("Create Ap logconf:")
   395      with open(yaml_manifest) as f:
   396          dep = yaml.safe_load(f)
   397      custom_objects.create_namespaced_custom_object(
   398          "appprotect.f5.com", "v1beta1", namespace, "aplogconfs", dep
   399      )
   400      print(f"AP logconf created with name '{dep['metadata']['name']}'")
   401      return dep["metadata"]["name"]
   402  
   403  
   404  def create_ap_policy_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> str:
   405      """
   406      Create a policy for AppProtect based on yaml file.
   407      :param custom_objects: CustomObjectsApi
   408      :param yaml_manifest: an absolute path to file
   409      :param namespace:
   410      :return: str
   411      """
   412      print("Create AP Policy:")
   413      with open(yaml_manifest) as f:
   414          dep = yaml.safe_load(f)
   415      custom_objects.create_namespaced_custom_object(
   416          "appprotect.f5.com", "v1beta1", namespace, "appolicies", dep
   417      )
   418      print(f"AP Policy created with name '{dep['metadata']['name']}'")
   419      return dep["metadata"]["name"]
   420  
   421  
   422  def create_ap_usersig_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> str:
   423      """
   424      Create a UserSig for AppProtect based on yaml file.
   425      :param custom_objects: CustomObjectsApi
   426      :param yaml_manifest: an absolute path to file
   427      :param namespace:
   428      :return: str
   429      """
   430      print("Create AP UserSig:")
   431      with open(yaml_manifest) as f:
   432          dep = yaml.safe_load(f)
   433      custom_objects.create_namespaced_custom_object(
   434          "appprotect.f5.com", "v1beta1", namespace, "apusersigs", dep
   435      )
   436      print(f"AP UserSig created with name '{dep['metadata']['name']}'")
   437      return dep["metadata"]["name"]
   438  
   439  
   440  def delete_and_create_ap_policy_from_yaml(
   441      custom_objects: CustomObjectsApi, name, yaml_manifest, namespace
   442  ) -> None:
   443      """
   444      Patch a AP Policy based on yaml manifest
   445      :param custom_objects: CustomObjectsApi
   446      :param name:
   447      :param yaml_manifest: an absolute path to file
   448      :param namespace:
   449      :return:
   450      """
   451      print(f"Update an AP Policy: {name}")
   452  
   453      try:
   454          delete_ap_policy(custom_objects, name, namespace)
   455          create_ap_policy_from_yaml(custom_objects, yaml_manifest, namespace)
   456      except ApiException:
   457          logging.exception(f"Failed with exception while patching AP Policy: {name}")
   458          raise
   459  
   460  
   461  def delete_ap_usersig(custom_objects: CustomObjectsApi, name, namespace) -> None:
   462      """
   463      Delete a AppProtect usersig.
   464      :param custom_objects: CustomObjectsApi
   465      :param namespace: namespace
   466      :param name:
   467      :return:
   468      """
   469      print(f"Delete AP UserSig: {name}")
   470      custom_objects.delete_namespaced_custom_object(
   471          "appprotect.f5.com", "v1beta1", namespace, "apusersigs", name
   472      )
   473      ensure_item_removal(
   474          custom_objects.get_namespaced_custom_object,
   475          "appprotect.f5.com",
   476          "v1beta1",
   477          namespace,
   478          "apusersigs",
   479          name,
   480      )
   481      print(f"AP UserSig was removed with name: {name}")
   482  
   483  
   484  def delete_ap_logconf(custom_objects: CustomObjectsApi, name, namespace) -> None:
   485      """
   486      Delete a AppProtect logconf.
   487      :param custom_objects: CustomObjectsApi
   488      :param namespace: namespace
   489      :param name:
   490      :return:
   491      """
   492      print(f"Delete AP logconf: {name}")
   493      custom_objects.delete_namespaced_custom_object(
   494          "appprotect.f5.com", "v1beta1", namespace, "aplogconfs", name
   495      )
   496      ensure_item_removal(
   497          custom_objects.get_namespaced_custom_object,
   498          "appprotect.f5.com",
   499          "v1beta1",
   500          namespace,
   501          "aplogconfs",
   502          name,
   503      )
   504      print(f"AP logconf was removed with name: {name}")
   505  
   506  
   507  def delete_ap_policy(custom_objects: CustomObjectsApi, name, namespace) -> None:
   508      """
   509      Delete a AppProtect policy.
   510      :param custom_objects: CustomObjectsApi
   511      :param namespace: namespace
   512      :param name:
   513      :return:
   514      """
   515      print(f"Delete a AP policy: {name}")
   516      custom_objects.delete_namespaced_custom_object(
   517          "appprotect.f5.com", "v1beta1", namespace, "appolicies", name
   518      )
   519      ensure_item_removal(
   520          custom_objects.get_namespaced_custom_object,
   521          "appprotect.f5.com",
   522          "v1beta1",
   523          namespace,
   524          "appolicies",
   525          name,
   526      )
   527      time.sleep(3)
   528      print(f"AP policy was removed with name: {name}")
   529  
   530  
   531  def delete_virtual_server(custom_objects: CustomObjectsApi, name, namespace) -> None:
   532      """
   533      Delete a VirtualServer.
   534  
   535      :param custom_objects: CustomObjectsApi
   536      :param namespace: namespace
   537      :param name:
   538      :return:
   539      """
   540      print(f"Delete a VirtualServer: {name}")
   541  
   542      custom_objects.delete_namespaced_custom_object(
   543          "k8s.nginx.org", "v1", namespace, "virtualservers", name
   544      )
   545      ensure_item_removal(
   546          custom_objects.get_namespaced_custom_object,
   547          "k8s.nginx.org",
   548          "v1",
   549          namespace,
   550          "virtualservers",
   551          name,
   552      )
   553      print(f"VirtualServer was removed with name '{name}'")
   554  
   555  
   556  def patch_virtual_server_from_yaml(
   557      custom_objects: CustomObjectsApi, name, yaml_manifest, namespace
   558  ) -> None:
   559      """
   560      Patch a VS based on yaml manifest
   561      :param custom_objects: CustomObjectsApi
   562      :param name:
   563      :param yaml_manifest: an absolute path to file
   564      :param namespace:
   565      :return:
   566      """
   567      print(f"Update a VirtualServer: {name}")
   568      with open(yaml_manifest) as f:
   569          dep = yaml.safe_load(f)
   570  
   571      try:
   572          custom_objects.patch_namespaced_custom_object(
   573              "k8s.nginx.org", "v1", namespace, "virtualservers", name, dep
   574          )
   575          print(f"VirtualServer updated with name '{dep['metadata']['name']}'")
   576      except ApiException:
   577          logging.exception(f"Failed with exception while patching VirtualServer: {name}")
   578          raise
   579  
   580  
   581  def patch_ts(
   582          custom_objects: CustomObjectsApi, name, yaml_manifest, namespace
   583  ) -> None:
   584      """
   585      Patch a TransportServer based on yaml manifest
   586      """
   587      return patch_custom_resource_v1alpha1(custom_objects, name, yaml_manifest, namespace, "transportservers")
   588  
   589  
   590  def patch_custom_resource_v1alpha1(custom_objects: CustomObjectsApi, name, yaml_manifest, namespace, plural) -> None:
   591      """
   592      Patch a custom resource based on yaml manifest
   593      """
   594      print(f"Update a Resource: {name}")
   595      with open(yaml_manifest) as f:
   596          dep = yaml.safe_load(f)
   597  
   598      try:
   599          custom_objects.patch_namespaced_custom_object(
   600              "k8s.nginx.org", "v1alpha1", namespace, plural, name, dep
   601          )
   602      except ApiException:
   603          logging.exception(f"Failed with exception while patching custom resource: {name}")
   604          raise
   605  
   606  
   607  def delete_and_create_vs_from_yaml(
   608      custom_objects: CustomObjectsApi, name, yaml_manifest, namespace
   609  ) -> None:
   610      """
   611      Perform delete and create for vs with same name based on yaml
   612  
   613      :param custom_objects: CustomObjectsApi
   614      :param name:
   615      :param yaml_manifest: an absolute path to file
   616      :param namespace:
   617      :return:
   618      """
   619      try:
   620          delete_virtual_server(custom_objects, name, namespace)
   621          create_virtual_server_from_yaml(custom_objects, yaml_manifest, namespace)
   622      except ApiException:
   623          logging.exception(f"Failed with exception while patching VirtualServer: {name}")
   624          raise
   625  
   626  
   627  def patch_virtual_server(custom_objects: CustomObjectsApi, name, namespace, body) -> str:
   628      """
   629      Update a VirtualServer based on a dict.
   630  
   631      :param custom_objects: CustomObjectsApi
   632      :param name:
   633      :param body: dict
   634      :param namespace:
   635      :return: str
   636      """
   637      print("Update a VirtualServer:")
   638      custom_objects.patch_namespaced_custom_object(
   639          "k8s.nginx.org", "v1", namespace, "virtualservers", name, body
   640      )
   641      print(f"VirtualServer updated with a name '{body['metadata']['name']}'")
   642      return body["metadata"]["name"]
   643  
   644  
   645  def patch_v_s_route_from_yaml(
   646      custom_objects: CustomObjectsApi, name, yaml_manifest, namespace
   647  ) -> None:
   648      """
   649      Update a VirtualServerRoute based on yaml manifest
   650  
   651      :param custom_objects: CustomObjectsApi
   652      :param name:
   653      :param yaml_manifest: an absolute path to file
   654      :param namespace:
   655      :return:
   656      """
   657      print(f"Update a VirtualServerRoute: {name}")
   658      with open(yaml_manifest) as f:
   659          dep = yaml.safe_load(f)
   660      try:
   661          custom_objects.patch_namespaced_custom_object(
   662              "k8s.nginx.org", "v1", namespace, "virtualserverroutes", name, dep
   663          )
   664          print(f"VirtualServerRoute updated with name '{dep['metadata']['name']}'")
   665      except ApiException:
   666          logging.exception(f"Failed with exception while patching VirtualServerRoute: {name}")
   667          raise
   668  
   669  
   670  def get_vs_nginx_template_conf(
   671      v1: CoreV1Api, vs_namespace, vs_name, pod_name, pod_namespace
   672  ) -> str:
   673      """
   674      Get contents of /etc/nginx/conf.d/vs_{namespace}_{vs_name}.conf in the pod.
   675  
   676      :param v1: CoreV1Api
   677      :param vs_namespace:
   678      :param vs_name:
   679      :param pod_name:
   680      :param pod_namespace:
   681      :return: str
   682      """
   683      file_path = f"/etc/nginx/conf.d/vs_{vs_namespace}_{vs_name}.conf"
   684      return get_file_contents(v1, file_path, pod_name, pod_namespace)
   685  
   686  
   687  def create_v_s_route_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> str:
   688      """
   689      Create a VirtualServerRoute based on a yaml file.
   690  
   691      :param custom_objects: CustomObjectsApi
   692      :param yaml_manifest: an absolute path to a file
   693      :param namespace:
   694      :return: str
   695      """
   696      print("Create a VirtualServerRoute:")
   697      with open(yaml_manifest) as f:
   698          dep = yaml.safe_load(f)
   699  
   700      custom_objects.create_namespaced_custom_object(
   701          "k8s.nginx.org", "v1", namespace, "virtualserverroutes", dep
   702      )
   703      print(f"VirtualServerRoute created with a name '{dep['metadata']['name']}'")
   704      return dep["metadata"]["name"]
   705  
   706  
   707  def patch_v_s_route(custom_objects: CustomObjectsApi, name, namespace, body) -> str:
   708      """
   709      Update a VirtualServerRoute based on a dict.
   710  
   711      :param custom_objects: CustomObjectsApi
   712      :param name:
   713      :param body: dict
   714      :param namespace:
   715      :return: str
   716      """
   717      print("Update a VirtualServerRoute:")
   718      custom_objects.patch_namespaced_custom_object(
   719          "k8s.nginx.org", "v1", namespace, "virtualserverroutes", name, body
   720      )
   721      print(f"VirtualServerRoute updated with a name '{body['metadata']['name']}'")
   722      return body["metadata"]["name"]
   723  
   724  
   725  def delete_v_s_route(custom_objects: CustomObjectsApi, name, namespace) -> None:
   726      """
   727      Delete a VirtualServerRoute.
   728  
   729      :param custom_objects: CustomObjectsApi
   730      :param namespace: namespace
   731      :param name:
   732      :return:
   733      """
   734      print(f"Delete a VirtualServerRoute: {name}")
   735      custom_objects.delete_namespaced_custom_object(
   736          "k8s.nginx.org", "v1", namespace, "virtualserverroutes", name,
   737      )
   738      ensure_item_removal(
   739          custom_objects.get_namespaced_custom_object,
   740          "k8s.nginx.org",
   741          "v1",
   742          namespace,
   743          "virtualserverroutes",
   744          name,
   745      )
   746      print(f"VirtualServerRoute was removed with the name '{name}'")
   747  
   748  
   749  def generate_item_with_upstream_options(yaml_manifest, options) -> dict:
   750      """
   751      Generate a VS/VSR item with an upstream option.
   752  
   753      Update all the upstreams in VS/VSR
   754      :param yaml_manifest: an absolute path to a file
   755      :param options: dict
   756      :return: dict
   757      """
   758      with open(yaml_manifest) as f:
   759          dep = yaml.safe_load(f)
   760      for upstream in dep["spec"]["upstreams"]:
   761          upstream.update(options)
   762      return dep