CermakM / argo-client-python

Python client for Argo Workflows | Mirrored to https://github.com/argoproj-labs/argo-client-python
https://github.com/argoproj/argo
Apache License 2.0
31 stars 8 forks source link

can not create a workflow `v2.5.0` #17

Closed brtasavpatel closed 4 years ago

brtasavpatel commented 4 years ago

I am trying out a simple example with workflow-client v2.5.0, here's my sample code. nothing fancy there

from argo.workflows.client import V1alpha1Api
from argo.workflows.config import load_kube_config
import requests
import yaml

load_kube_config()  # loads local configuration from ~/.kube/confi
v1alpha1 = V1alpha1Api()

namespace = "argo"
# hello-world example
resp = requests.get("https://raw.githubusercontent.com/argoproj/argo/master/examples/hello-world.yaml")
resp.raise_for_status()
manifest: dict = yaml.safe_load(resp.text)
v1alpha1.create_namespaced_workflow(namespace, manifest)

I keep getting

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-19-52eeb891da41> in <module>
----> 1 v1alpha1.create_namespaced_workflow(namespace, manifest)

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api/v1alpha1_api.py in create_namespaced_workflow(self, namespace, body, **kwargs)
    261             return self.create_namespaced_workflow_with_http_info(namespace, body, **kwargs)  # noqa: E501
    262         else:
--> 263             (data) = self.create_namespaced_workflow_with_http_info(namespace, body, **kwargs)  # noqa: E501
    264             return data
    265

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api/v1alpha1_api.py in create_namespaced_workflow_with_http_info(self, namespace, body, **kwargs)
    342             _preload_content=params.get('_preload_content', True),
    343             _request_timeout=params.get('_request_timeout'),
--> 344             collection_formats=collection_formats)
    345
    346     def create_namespaced_workflowtemplate(self, namespace, body, **kwargs):  # noqa: E501

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in call_api(self, resource_path, method, path_params, query_params, header_params, body, post_params, files, response_type, auth_settings, async_req, _return_http_data_only, collection_formats, _preload_content, _request_timeout)
    328                                    response_type, auth_settings,
    329                                    _return_http_data_only, collection_formats,
--> 330                                    _preload_content, _request_timeout)
    331         else:
    332             thread = self.pool.apply_async(self.__call_api, (resource_path,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __call_api(self, resource_path, method, path_params, query_params, header_params, body, post_params, files, response_type, auth_settings, _return_http_data_only, collection_formats, _preload_content, _request_timeout)
    167             # deserialize response data
    168             if response_type:
--> 169                 return_data = self.deserialize(response_data, response_type)
    170             else:
    171                 return_data = None

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in deserialize(self, response, response_type)
    239             data = response.data
    240
--> 241         return self.__deserialize(data, response_type)
    242
    243     def __deserialize(self, data, klass):

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize(self, data, klass)
    278             return self.__deserialize_datatime(data)
    279         else:
--> 280             return self.__deserialize_model(data, klass)
    281
    282     def call_api(self, resource_path, method,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize_model(self, data, klass)
    622                         isinstance(data, (list, dict))):
    623                     value = data[klass.attribute_map[attr]]
--> 624                     kwargs[attr] = self.__deserialize(value, attr_type)
    625
    626         instance = klass(**kwargs)

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize(self, data, klass)
    278             return self.__deserialize_datatime(data)
    279         else:
--> 280             return self.__deserialize_model(data, klass)
    281
    282     def call_api(self, resource_path, method,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize_model(self, data, klass)
    622                         isinstance(data, (list, dict))):
    623                     value = data[klass.attribute_map[attr]]
--> 624                     kwargs[attr] = self.__deserialize(value, attr_type)
    625
    626         instance = klass(**kwargs)

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize(self, data, klass)
    256                 sub_kls = re.match(r'list\[(.*)\]', klass).group(1)
    257                 return [self.__deserialize(sub_data, sub_kls)
--> 258                         for sub_data in data]
    259
    260             if klass.startswith('dict('):

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in <listcomp>(.0)
    256                 sub_kls = re.match(r'list\[(.*)\]', klass).group(1)
    257                 return [self.__deserialize(sub_data, sub_kls)
--> 258                         for sub_data in data]
    259
    260             if klass.startswith('dict('):

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize(self, data, klass)
    278             return self.__deserialize_datatime(data)
    279         else:
--> 280             return self.__deserialize_model(data, klass)
    281
    282     def call_api(self, resource_path, method,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize_model(self, data, klass)
    622                         isinstance(data, (list, dict))):
    623                     value = data[klass.attribute_map[attr]]
--> 624                     kwargs[attr] = self.__deserialize(value, attr_type)
    625
    626         instance = klass(**kwargs)

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize(self, data, klass)
    278             return self.__deserialize_datatime(data)
    279         else:
--> 280             return self.__deserialize_model(data, klass)
    281
    282     def call_api(self, resource_path, method,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize_model(self, data, klass)
    624                     kwargs[attr] = self.__deserialize(value, attr_type)
    625
--> 626         instance = klass(**kwargs)
    627
    628         if (isinstance(instance, dict) and

~/Workspace/tartarus/backend/tartarus-env/lib/python3.7/site-packages/kubernetes/client/models/v1_container.py in __init__(self, args, command, env, env_from, image, image_pull_policy, lifecycle, liveness_probe, name, ports, readiness_probe, resources, security_context, stdin, stdin_once, termination_message_path, termination_message_policy, tty, volume_devices, volume_mounts, working_dir)
    123         if liveness_probe is not None:
    124           self.liveness_probe = liveness_probe
--> 125         self.name = name
    126         if ports is not None:
    127           self.ports = ports

~/Workspace/tartarus/backend/tartarus-env/lib/python3.7/site-packages/kubernetes/client/models/v1_container.py in name(self, name)
    354         """
    355         if name is None:
--> 356             raise ValueError("Invalid value for `name`, must not be `None`")
    357
    358         self._name = name

ValueError: Invalid value for `name`, must not be `None`

I installed argo-workflows client using

pip install -e "git+git://github.com/CermakM/argo-client-python@argo/v2.5.0#egg=argo-workflows"

My environment info :

pip list | grep -i 'kubernetes\|argo'
argo-workflows           3.0.0rc0   
kubernetes               10.0.1

kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:16:51Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}

if I add name to my container definition

manifest['spec']['templates'][0]['container']['name'] = "test_workflow"

then I get following error :

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-20-52eeb891da41> in <module>
----> 1 v1alpha1.create_namespaced_workflow(namespace, manifest)

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api/v1alpha1_api.py in create_namespaced_workflow(self, namespace, body, **kwargs)
    261             return self.create_namespaced_workflow_with_http_info(namespace, body, **kwargs)  # noqa: E501
    262         else:
--> 263             (data) = self.create_namespaced_workflow_with_http_info(namespace, body, **kwargs)  # noqa: E501
    264             return data
    265

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api/v1alpha1_api.py in create_namespaced_workflow_with_http_info(self, namespace, body, **kwargs)
    342             _preload_content=params.get('_preload_content', True),
    343             _request_timeout=params.get('_request_timeout'),
--> 344             collection_formats=collection_formats)
    345
    346     def create_namespaced_workflowtemplate(self, namespace, body, **kwargs):  # noqa: E501

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in call_api(self, resource_path, method, path_params, query_params, header_params, body, post_params, files, response_type, auth_settings, async_req, _return_http_data_only, collection_formats, _preload_content, _request_timeout)
    328                                    response_type, auth_settings,
    329                                    _return_http_data_only, collection_formats,
--> 330                                    _preload_content, _request_timeout)
    331         else:
    332             thread = self.pool.apply_async(self.__call_api, (resource_path,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __call_api(self, resource_path, method, path_params, query_params, header_params, body, post_params, files, response_type, auth_settings, _return_http_data_only, collection_formats, _preload_content, _request_timeout)
    167             # deserialize response data
    168             if response_type:
--> 169                 return_data = self.deserialize(response_data, response_type)
    170             else:
    171                 return_data = None

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in deserialize(self, response, response_type)
    239             data = response.data
    240
--> 241         return self.__deserialize(data, response_type)
    242
    243     def __deserialize(self, data, klass):

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize(self, data, klass)
    278             return self.__deserialize_datatime(data)
    279         else:
--> 280             return self.__deserialize_model(data, klass)
    281
    282     def call_api(self, resource_path, method,

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/api_client.py in __deserialize_model(self, data, klass)
    624                     kwargs[attr] = self.__deserialize(value, attr_type)
    625
--> 626         instance = klass(**kwargs)
    627
    628         if (isinstance(instance, dict) and

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/models/v1alpha1_workflow.py in __init__(self, api_version, kind, metadata, spec, status)
     64         self.metadata = metadata
     65         self.spec = spec
---> 66         self.status = status
     67
     68     @property

~/Workspace/tartarus/backend/tartarus-env/src/argo-models/argo/workflows/client/models/v1alpha1_workflow.py in status(self, status)
    177         """
    178         if status is None:
--> 179             raise ValueError("Invalid value for `status`, must not be `None`")  # noqa: E501
    180
    181         self._status = status
yxue-kabam commented 4 years ago

I deleted my previous comment because I wanted to verify what you got before I reach my conclusion. I can reproduce the error and confirmed this is indeed what issue #11 had. i.e. V1Container does not expect name attr to be empty. With that fixed, you also need to add V1alpha1WorkflowStatus() to manifest['status'].

i.e. to get your workflow working, do the following before calling create_namespaced_workflow

>>> manifest['status'] = V1alpha1WorkflowStatus().to_dict()
>>> manifest['spec']['templates'][0]['container']['name'] = ''

I recommend checking out the https://github.com/CermakM/argo-python-dsl which is more user-friendly, while this library is a direct generation of Argo's swagger api spec.

brtasavpatel commented 4 years ago

@yxue-kabam thanks for your reply your workaround works at the moment. I definitely want to usedsl but I can't get it to work also. here's an Issue I have opened. https://github.com/CermakM/argo-python-dsl/issues/7

CermakM commented 4 years ago

@brtasavpatel Thanks for the thorough report! I believe your issue is related to #15 where I explain why name and status are required and has to be present.

TLDR; it is due to API specification, this library nor the DSL has any part in that. A workaround is, as described by @yxue-kabam (thanks man!) is for a name to use name = "" (but it is a good practice to actually provide the name) and for the status = {} (it is BS that status is required, but what can we do...)

Cheers, M