class_docstr: cluster
  
  This is meant as a thin wrapper around GKE clusters and the raw kubernetes
  python api, mainly to provide a simple interface for the most common
  cluster tasks.
class_docstr_tok: ["cluster","This","is","meant","as","a","thin","wrapper","around","GKE","clusters","and","the","raw","kubernetes","python","api",",","mainly","to","provide","a","simple","interface","for","the","most","common","cluster","tasks","."]
docstr: creates a simple kubernetes job (1 container, 1 pod) JobSpec for this cluster
  
  Args:
  name: job name
  image: container image url (gcr.io/...)
  min_cpu: minimum cpu needed, in milli-cpu
  min_mem: minimum memory needed, in MB
  command: command to execute, None = container entrypoint
  args: args to pass to command
  env: environment vars for container
  accelerator: accelerator type, None=cpu only
  accelerator_count: accelerator count
  namespace: kubernetes namespace
  machine_type: machine type, None=default for mode (cpu/gpu)
  preemptible: use preemptible instance
  preemptible_tpu: use preemptible tpus
  tpu_driver: tpu driver to use
  labels: user labels to set
  caliban_config: caliban configuration dictionary
  
  Returns:
  JobSpec on success, None otherwise
docstr_tok: ["creates","a","simple","kubernetes","job","(","1","container",",","1","pod",")","JobSpec","for","this","cluster","Args",":","name",":","job","name","image",":","container","image","url","(","gcr.io\/","...",")","min_cpu",":","minimum","cpu","needed",",","in","milli-cpu","min_mem",":","minimum","memory","needed",",","in","MB","command",":","command","to","execute",",","None","=","container","entrypoint","args",":","args","to","pass","to","command","env",":","environment","vars","for","container","accelerator",":","accelerator","type",",","None=cpu","only","accelerator_count",":","accelerator","count","namespace",":","kubernetes","namespace","machine_type",":","machine","type",",","None=default","for","mode","(","cpu\/gpu",")","preemptible",":","use","preemptible","instance","preemptible_tpu",":","use","preemptible","tpus","tpu_driver",":","tpu","driver","to","use","labels",":","user","labels","to","set","caliban_config",":","caliban","configuration","dictionary","Returns",":","JobSpec","on","success",",","None","otherwise"]
code: def create_simple_job_spec(
      self,
      experiment: Experiment,
      name: str,
      image: str,
      min_cpu: int,
      min_mem: int,
      index: int,
      command: Optional[List[str]] = None,
      env: Dict[str, str] = {},
      accelerator: Optional[Accelerator] = None,
      accelerator_count: int = 1,
      namespace: str = k.DEFAULT_NAMESPACE,
      machine_type: Optional[MachineType] = None,
      preemptible: bool = True,
      preemptible_tpu: bool = True,
      tpu_driver: str = k.DEFAULT_TPU_DRIVER,
      labels: Optional[Dict[str, str]] = None,
      caliban_config: Optional[Dict[str, Any]] = None,
  ) -> Optional[JobSpec]:
      """creates a simple kubernetes job (1 container, 1 pod) JobSpec for this cluster
  
      Args:
      name: job name
      image: container image url (gcr.io/...)
      min_cpu: minimum cpu needed, in milli-cpu
      min_mem: minimum memory needed, in MB
      command: command to execute, None = container entrypoint
      args: args to pass to command
      env: environment vars for container
      accelerator: accelerator type, None=cpu only
      accelerator_count: accelerator count
      namespace: kubernetes namespace
      machine_type: machine type, None=default for mode (cpu/gpu)
      preemptible: use preemptible instance
      preemptible_tpu: use preemptible tpus
      tpu_driver: tpu driver to use
      labels: user labels to set
      caliban_config: caliban configuration dictionary
  
      Returns:
      JobSpec on success, None otherwise
      """
  
      caliban_config = caliban_config or {}
      labels = labels or {}
  
      launcher_args = um.mlflow_args(
          caliban_config=caliban_config,
          experiment_name=experiment.xgroup.name,
          index=index,
          tags={
              um.PLATFORM_TAG: Platform.GKE.value,
              **labels,
          },
      )
  
      cmd_args = ce.experiment_to_args(
          experiment.kwargs, experiment.args
      )
  
      # cmd args *must* be last in order for the launcher to pass them through
      args = launcher_args + cmd_args
  
      # ------------------------------------------------------------------------
      # container
  
      # tpu/gpu resources
      container_resources = V1ResourceRequirements(
          requests=Cluster.container_requests(min_cpu, min_mem),
          limits=Cluster.container_limits(
              accelerator,
              accelerator_count,
              preemptible_tpu,
          ),
      )
  
      container_env = [
          V1EnvVar(name=k, value=v) for k, v in env.items()
      ]
  
      # this is a simple 1-container, 1-pod job, so we just name the
      # container the same thing (minus the generated suffix) as the job itself
      container = V1Container(
          name=name,
          image=image,
          command=command,
          args=args,
          resources=container_resources,
          env=container_env,
          image_pull_policy="Always",
      )
  
      # ------------------------------------------------------------------------
      # template
  
      # todo: should we support anything other than a 'never' restart policy?
      # see this for discussion
      # https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#pod-backoff-failure-policy
  
      tolerations = Cluster.tolerations(preemptible=preemptible)
  
      # backoff count plus 'OnFailure' may be correct here
      template_spec = V1PodSpec(
          restart_policy="Never",
          containers=[container],
          tolerations=tolerations,
          node_selector=Cluster.node_selector(
              preemptible=preemptible,
              machine_type=machine_type,
              accelerator=accelerator,
          ),
          host_ipc=True,
      )
  
      template = V1PodTemplateSpec(
          metadata=Cluster.template_metadata(
              accelerator=accelerator,
              tpu_driver=tpu_driver,
          ),
          spec=template_spec,
      )
  
      # ------------------------------------------------------------------------
      # job
      job_spec = V1JobSpec(template=template, backoff_limit=4)
  
      return JobSpec.get_or_create(
          experiment=experiment,
          spec=ApiClient().sanitize_for_serialization(job_spec),
          platform=Platform.GKE,
      )
  
imports: import json
  import logging
  import re
  from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
  import googleapiclient
  import kubernetes
  import requests
  import urllib3
  import yaml
  from google.auth.credentials import Credentials
  from google.cloud.container_v1 import ClusterManagerClient
  from google.cloud.container_v1.types import NodePool, Cluster
  from googleapiclient import discovery
  from googleapiclient.http import HttpRequest
  from kubernetes.client import V1Container, V1DaemonSet, V1EnvVar, V1Job, V1JobSpec, V1ObjectMeta, V1Pod, V1PodSpec, V1PodTemplateSpec, V1ResourceRequirements, V1Toleration
  from kubernetes.client.api_client import ApiClient
  import caliban.config.experiment
  import caliban.platform.gke.constants
  import caliban.platform.gke.util
  from caliban.history.types import Experiment, Job, JobSpec, JobStatus, Platform
  from caliban.platform.cloud.types import GPU, TPU, Accelerator, GPUSpec, MachineType, TPUSpec
  from caliban.platform.gke.types import NodeImage, OpStatus, ReleaseChannel
  from caliban.platform.gke.util import trap
  import caliban.util.metrics
label_desc: Use image node_id 20 for calling the Cluster obj's underlying member method code with example usage: obj.create_simple_job_spec(experiment, name, image, min_cpu, min_mem, index, command, env, accelerator, accelerator_count, namespace, machine_type, preemptible, preemptible_tpu, tpu_driver, labels, caliban_config) and returns: JobSpec