Python: Unable to create jobs

Created on 2 Aug 2018  路  13Comments  路  Source: kubernetes-client/python

I'm trying the client, but I run into several issues, the main issue at the moment is that I'm unable to create jobs with the client, the code:

import kubernetes.client
from kubernetes import config
from pprint import pprint

config.load_kube_config()

job_name = 'test'

api_instance = kubernetes.client.BatchV1Api()
namespace = 'default'

body = kubernetes.client.V1Job()
body.metadata = kubernetes.client.V1ObjectMeta(name=job_name)
template = kubernetes.client.V1PodTemplate()
template.template = kubernetes.client.V1PodTemplateSpec()
container = kubernetes.client.V1Container(name="busybox")
container.image = "busybox"
container.args = ["sleep", "10"]
container.restart_policy = 'Never'
template.template.spec = kubernetes.client.V1PodSpec(restart_policy='Never', containers=[container])
body.spec = kubernetes.client.V1JobSpec(template=template, backoff_limit=0)
print(str(body))

api_response = api_instance.create_namespaced_job(namespace, body, pretty=True)
pprint(api_response)

The output:

{'api_version': None,
 'kind': None,
 'metadata': {'annotations': None,
              'cluster_name': None,
              'creation_timestamp': None,
              'deletion_grace_period_seconds': None,
              'deletion_timestamp': None,
              'finalizers': None,
              'generate_name': None,
              'generation': None,
              'initializers': None,
              'labels': None,
              'name': 'test',
              'namespace': None,
              'owner_references': None,
              'resource_version': None,
              'self_link': None,
              'uid': None},
 'spec': {'active_deadline_seconds': None,
          'backoff_limit': 0,
          'completions': None,
          'manual_selector': None,
          'parallelism': None,
          'selector': None,
          'template': {'api_version': None,
                       'kind': None,
                       'metadata': None,
                       'template': {'metadata': None,
                                    'spec': {'active_deadline_seconds': None,
                                             'affinity': None,
                                             'automount_service_account_token': None,
                                             'containers': [{'args': ['sleep',
                                                                      '10'],
                                                             'command': None,
                                                             'env': None,
                                                             'env_from': None,
                                                             'image': 'busybox',
                                                             'image_pull_policy': None,
                                                             'lifecycle': None,
                                                             'liveness_probe': None,
                                                             'name': 'busybox',
                                                             'ports': None,
                                                             'readiness_probe': None,
                                                             'resources': None,
                                                             'security_context': None,
                                                             'stdin': None,
                                                             'stdin_once': None,
                                                             'termination_message_path': None,
                                                             'termination_message_policy': None,
                                                             'tty': None,
                                                             'volume_devices': None,
                                                             'volume_mounts': None,
                                                             'working_dir': None}],
                                             'dns_config': None,
                                             'dns_policy': None,
                                             'host_aliases': None,
                                             'host_ipc': None,
                                             'host_network': None,
                                             'host_pid': None,
                                             'hostname': None,
                                             'image_pull_secrets': None,
                                             'init_containers': None,
                                             'node_name': None,
                                             'node_selector': None,
                                             'priority': None,
                                             'priority_class_name': None,
                                             'readiness_gates': None,
                                             'restart_policy': 'Never',
                                             'scheduler_name': None,
                                             'security_context': None,
                                             'service_account': None,
                                             'service_account_name': None,
                                             'share_process_namespace': None,
                                             'subdomain': None,
                                             'termination_grace_period_seconds': None,
                                             'tolerations': None,
                                             'volumes': None}}}},
 'status': None}
Traceback (most recent call last):
  File "job.py", line 32, in <module>
    api_response = api_instance.create_namespaced_job(namespace, body, pretty=True)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/apis/batch_v1_api.py", line 58, in create_namespaced_job
    (data) = self.create_namespaced_job_with_http_info(namespace, body, **kwargs)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/apis/batch_v1_api.py", line 143, in create_namespaced_job_with_http_info
    collection_formats=collection_formats)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/api_client.py", line 321, in call_api
    _return_http_data_only, collection_formats, _preload_content, _request_timeout)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/api_client.py", line 155, in __call_api
    _request_timeout=_request_timeout)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/api_client.py", line 364, in request
    body=body)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/rest.py", line 266, in POST
    body=body)
  File "/home/paul/anaconda3/envs/kubeapi-psike/lib/python3.7/site-packages/kubernetes/client/rest.py", line 222, in request
    raise ApiException(http_resp=r)
kubernetes.client.rest.ApiException: (422)
Reason: Unprocessable Entity
HTTP response headers: HTTPHeaderDict({'Content-Type': 'application/json', 'Date': 'Thu, 02 Aug 2018 17:57:25 GMT', 'Content-Length': '789'})
HTTP response body: {
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "Job.batch \"test\" is invalid: [spec.template.spec.containers: Required value, spec.template.spec.restartPolicy: Unsupported value: \"Always\": supported values: \"OnFailure\", \"Never\"]",
  "reason": "Invalid",
  "details": {
    "name": "test",
    "group": "batch",
    "kind": "Job",
    "causes": [
      {
        "reason": "FieldValueRequired",
        "message": "Required value",
        "field": "spec.template.spec.containers"
      },
      {
        "reason": "FieldValueNotSupported",
        "message": "Unsupported value: \"Always\": supported values: \"OnFailure\", \"Never\"",
        "field": "spec.template.spec.restartPolicy"
      }
    ]
  },
  "code": 422
}

basicly it ignores the actually restartPolicy, or I am doing something wrong.

The underlying issue is documentation and inconsistency:

  • inconsisten behavior: in some cases you need to pass in parameters in the contructor of the object, in some cases you can assign it to a property after creating the object
  • some of the examples are not working
  • the docs of the api client are not that helpful in some cases (I managed to do something with configmaps and listing pods, but I can't create a job at all)
  • it's hard to translate a yaml file into the correct objects

Most helpful comment

@shanit-saha It sounds like you still read it into an object?
What I do:

import ruamel.yaml
# Load yaml file
with open('template.yaml') as fo:
    job = ruamel.yaml.safe_load(fo)
# ... manipulate python dict, for example set the name
job['metadata']['name'] = 'test'
# Send
api.create_namespaced_job(
    job['metadata']['namespace'], job, pretty=True,
    _request_timeout=(settings.CONNECTION_TIMEOUT_SECONDS, settings.READ_TIMEOUT_SECONDS)
)

All 13 comments

It seems to be an issue with the objects, if I send a dictionary it is fine. It might have to do with template.template.spec in the code, but if I use template.spec, it won't be included in the resulting dictionary.

I too am stuck with the same problem as reported by @pvanderlinden . By any chance is there a solution available. My use case demands to create Environment variables on the run time when I am creating the K8-Job / Pod. We are creating K8-jobs from an application flow. using YAML doesn't seem to be flexible to do this.

@shanit-saha What I found working the best so far is using a yaml file as template, then with python code fill in the blanks (as it is just basic python structures then (dicts, lists, strings, etc)

@pvanderlinden : Thank You ! for the response and advise.
When I am trying to read the YML template and use it the way you have stated I am getting the error as below

'BatchV1Api' object has no attribute 'select_header_accept'

================YAML is As below======================================

apiVersion: batch/v1
kind: Job
metadata:
name: dx3-job-test-4
spec:
ttlSecondsAfterFinished: 2
backoffLimit: 4
completions: 1
parallelism: 1
template:
metadata:
name: dx3-job-4
spec:
containers:
- image: "19.0.11.77/ux3user/lab-testdeployment:v1"
name: counter
ports:
- containerPort: 5035
volumeMounts:
- mountPath: /mnt/nfs/dir
name: dx-pv-volume1
resources:
requests:
cpu: 100m
memory: 100Mi
terminationGracePeriodSeconds: 5
restartPolicy: Never
volumes:
- name: dx-pv-volume1
persistentVolumeClaim:
claimName: dx-pv-claim1

@shanit-saha It sounds like you still read it into an object?
What I do:

import ruamel.yaml
# Load yaml file
with open('template.yaml') as fo:
    job = ruamel.yaml.safe_load(fo)
# ... manipulate python dict, for example set the name
job['metadata']['name'] = 'test'
# Send
api.create_namespaced_job(
    job['metadata']['namespace'], job, pretty=True,
    _request_timeout=(settings.CONNECTION_TIMEOUT_SECONDS, settings.READ_TIMEOUT_SECONDS)
)

Same here,

Still there is resolution?

@dsever : I had no issues to create pod and other stuff with the python kubernetes API except for the job object. I have no knowledge if a fix or soultion available with the K8-Api for Job object.
However going by the advice of @pvanderlinden above helped to resolve my problem. Going by his advice this is what I did
a) created a bare minimum K8-Yml specification for a Job . For e.g. 'create_k8job_v1.yml' saved it to a location.
b) Then read that YML with the help of 'ruamel.yaml' object. This returns a JSON dictionary.
c) Then set the values of the K8 job specification as per the usecase needs.
d) Pass the final set to JobBatchClient.create_namespaced_job().

The sample python code synopsys below I think should help.

import ruamel.yaml # assuming a pip install is done :-)
objConfig = config.load_kube_config(path.join(environ["HOME"], '.kube/config'))
k8JobBatchClient = client.BatchV1Api(kubernetes.client.ApiClient(objConfig))
strK8YmlTemplatePath = "/dir-abc/code/k8Job/create_k8job_v1.yml"
fo = open(strK8YmlTemplatePath)
objJob = ruamel.yaml.safe_load(fo)
# Set the custom values of the to-be K8-Job as below
objJob ['metadata']['name'] = "k8-Job-JingaLaLa"  # Job Name
objJob ['spec']['template']['spec']['containers'][0]['name'] = "Provide Some Valid ContainerName Here"  # Container Name
objJob ['spec']['template']['spec']['containers'][0]['image'] = "Provide some valid Container-Image name"
objJob ['spec']['template']['spec']['containers'][0]['resources']['requests']['cpu'] = '200m'
objJob ['spec']['template']['spec']['containers'][0]['resources']['requests']['memory'] = '512Mi'
# Finally the lines below to create the k8-Job
executionResult = self._k8JobBatchClient.create_namespaced_job("NameSpaceDataPipe", objJob)
# The execution result "executionResult " will be a JSON dictionary

At the end I resolved problem by creating spec key manually.
https://github.com/kubernetes-client/python/issues/755

Fix, should I submit a PR?

def __deserialize_model(self, data, klass):
        """
        Deserializes list or dict to model.
        :param data: dict, list.
        :param klass: class literal.
        :return: model object.
        """

        if not klass.swagger_types and not hasattr(klass, 'get_real_child_model'):
            return data

        kwargs = {}
        if klass.swagger_types is not None:
            for attr, attr_type in iteritems(klass.swagger_types):
                if data is not None \
                   # instead of: klass.attribute_map[attr] in data
                   and attr in data \
                   and isinstance(data, (list, dict)):
                    # instead of: value = data[klass.attribute_map[attr]]
                    value = data[attr]
                    kwargs[attr] = self.__deserialize(value, attr_type)

        instance = klass(**kwargs)

        if hasattr(instance, 'get_real_child_model'):
            klass_name = instance.get_real_child_model(data)
            if klass_name:
                instance = self.__deserialize(data, klass_name)
        return instance

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle stale

File "/home/koder/workspace/ceph-kaas-controller/ceph_controller/__main__.py", line 344, in set_node_labels
for node in core_api.list_node().items:
File "/home/koder/workspace/venvs/k8s/lib/python3.7/site-packages/kubernetes/client/apis/core_v1_api.py", line 13392, in list_node
(data) = self.list_node_with_http_info(**kwargs)
File "/home/koder/workspace/venvs/k8s/lib/python3.7/site-packages/kubernetes/client/apis/core_v1_api.py", line 13466, in list_node_with_http_info
header_params['Accept'] = self.api_client.\
AttributeError: 'ExtensionsV1beta1Api' object has no attribute 'select_header_accept'

during CoreV1Api().list_nodes()
kubernetes==10.0.0

/remove-lifecycle stale

for https://github.com/kubernetes-client/python/issues/589#issue-347117150, the error message is from apiserver as the request is invalid, job only supports Never or OnFailure as restart policy https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#writing-a-job-spec (although pod template has an option Always, it's not allowed in job)

I see we are lacking documentation/examples around job creation. I put together a small example in https://github.com/kubernetes-client/python/issues/872#issuecomment-511988833. Contribution to improve our documentation/examples is welcome

Closing as the original question was intended behavior

Was this page helpful?
0 / 5 - 0 ratings