openshift/openshift-restclient-python

apply method didn't work with provided namespace

ahrechanychenko opened this issue · 8 comments

(Pdb) from kubernetes import config
(Pdb) from openshift.dynamic import DynamicClient
(Pdb) k8s_client = config.new_client_from_config()
(Pdb) dyn_client = DynamicClient(k8s_client)
(Pdb) data = yaml.safe_load(open("test.yaml"))
(Pdb) data
{'kind': 'Service', 'spec': {'ports': [{'targetPort': 8080, 'protocol': 'TCP', 'port': 8080, 'name': 'web3'}]}, 'apiVersion': 'v1', 'metadata': {'name': 'svc-test'}}
(Pdb) resp = dyn_client.apply(service, body=data, namespace="test")

 PDB set_trace (IO-capturing turned off) 
*** ValueError: Namespace is required for v1.Service

(Pdb) data['metadata']['namespace']="test"
(Pdb) data
{'kind': 'Service', 'spec': {'ports': [{'targetPort': 8080, 'protocol': 'TCP', 'port': 8080, 'name': 'web3'}]}, 'apiVersion': 'v1', 'metadata': {'namespace': 'test', 'name': 'svc-test'}}
(Pdb) resp = dyn_client.apply(service, body=data)
(Pdb) resp
ResourceInstance[Service]:
  apiVersion: v1
  kind: Service
  metadata:
    creationTimestamp: '2019-05-23T11:56:08Z'
    name: svc-test
    namespace: test
    resourceVersion: '3133190'
    selfLink: /api/v1/namespaces/test/services/svc-test
    uid: c066a908-7d51-11e9-a348-5254002c416c
  spec:
    clusterIP: 172.30.186.221
    ports:
    - name: web3
      port: 8080
      protocol: TCP
      targetPort: 8080
    sessionAffinity: None
    type: ClusterIP
  status:
    loadBalancer: {}

Cannot reproduce on latest master branch. I had to modify yours slightly to actually ensure that service got defined.

Can you do print openshift.__version__ ?

from kubernetes import config
from openshift.dynamic import DynamicClient
import yaml

k8s_client = config.new_client_from_config()
dyn_client = DynamicClient(k8s_client)
data = yaml.safe_load(open("service.yaml"))
service = dyn_client.resources.get(api_version='v1', kind='Service')
resp = dyn_client.apply(service, body=data, namespace="test")
$ kubectl get svc -n test -o yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: '{"kind":"Service","spec":{"ports":[{"targetPort":8080,"protocol":"TCP","port":8080,"name":"web3"}]},"apiVersion":"v1","metadata":{"namespace":"test","name":"svc-test"}}'
    creationTimestamp: 2019-05-23T13:17:42Z
    name: svc-test
    namespace: test
    resourceVersion: "121618"
    selfLink: /api/v1/namespaces/test/services/svc-test
    uid: 2553598b-7d5d-11e9-b584-025000000001
  spec:
    clusterIP: 10.106.90.9
    ports:
    - name: web3
      port: 8080
      protocol: TCP
      targetPort: 8080
    sessionAffinity: None
    type: ClusterIP
  status:
    loadBalancer: {}
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

0.10.0dev1

from pip I get next
print openshift.version
0.8.8
AttributeError: 'DynamicClient' object has no attribute 'apply'

and are you on the latest commit from the master branch?

if so, can you rerun my test and see if you get the same results (my service.yml should be the same as your test.yml) - if you don't, can you see what in your code differs from mine (or just paste the code exactly - I'm not really sure how yours ever worked without service being defined or without import yaml)

(openshift) [root@titan46 ~]# cat test.py
from kubernetes import config
from openshift.dynamic import DynamicClient
import yaml

k8s_client = config.new_client_from_config()
dyn_client = DynamicClient(k8s_client)

service = dyn_client.resources.get(api_version='v1', kind="Service")
data = yaml.safe_load(open("test.yaml"))
resp = dyn_client.apply(service, body=data, namespace="test")

(openshift) [root@titan46 ~]# python test.py
Traceback (most recent call last):
File "test.py", line 10, in
resp = dyn_client.apply(service, body=data, namespace="test")
File "/root/openshift/lib/python2.7/site-packages/openshift/dynamic/client.py", line 187, in apply
return apply(resource, body)
File "/root/openshift/lib/python2.7/site-packages/openshift/dynamic/apply.py", line 19, in apply
return resource.create(body=dict_merge(definition, desired_annotation), namespace=definition['metadata'].get('namespace'))
File "/root/openshift/lib/python2.7/site-packages/openshift/dynamic/client.py", line 77, in inner
resp = func(self, resource, *args, **kwargs)
File "/root/openshift/lib/python2.7/site-packages/openshift/dynamic/client.py", line 142, in create
namespace = self.ensure_namespace(resource, namespace, body)
File "/root/openshift/lib/python2.7/site-packages/openshift/dynamic/client.py", line 125, in ensure_namespace
raise ValueError("Namespace is required for {}.{}".format(resource.group_version, resource.kind))
ValueError: Namespace is required for v1.Service

(openshift) [root@titan46 openshift-restclient-python]# git log -n 1
commit 4376f93
Author: Fabian von Feilitzsch fabian@fabianism.us
Date: Wed May 22 16:13:10 2019 -0400

always use merge-patch+json in apply (#294)

I honestly have no idea - as I say, I can't reproduce.

Things I would try:

  • add print body before the raise ValueError in client.py
  • add print data just after the yaml.load in your script

That should show that either data is wrong (an input problem) or it gets mangled before ensure_namespace.

(test) []# cat test.py

from openshift.dynamic import DynamicClient
import yaml

k8s_client = config.new_client_from_config()
dyn_client = DynamicClient(k8s_client)

service = dyn_client.resources.get(api_version='v1', kind="Service")
data = yaml.safe_load(open("test.yaml"))
print("content of data")
print(data)
resp = dyn_client.apply(service, body=data, namespace="test")'''

###updated func
(test) [root@titan46 ~]# grep "def apply(self, resource, body=None, name=None, namespace=None):" /tmp/test/lib/python2.7/site-packages/openshift/dynamic/client.py -A14

    def apply(self, resource, body=None, name=None, namespace=None):
        print("print body before serialization")
        print(body)
        body = self.serialize_body(body)
        print("after serialization")
        print(body)
        name = name or body.get('metadata', {}).get('name')
        if not name:
            raise ValueError("name is required to apply {}.{}".format(resource.group_version, resource.kind))
        if resource.namespaced:
            namespace = self.ensure_namespace(resource, namespace, body)
            print("namespace from apply_func")
            print(namespace)
        return apply(resource, body)

(test) [~]# python test.py
content of data
{'kind': 'Service', 'spec': {'ports': [{'targetPort': 8080, 'protocol': 'TCP', 'port': 8080, 'name': 'web3'}]}, 'apiVersion': 'v1', 'metadata': {'name': 'svc-test'}}
print body before serialization
{'kind': 'Service', 'spec': {'ports': [{'targetPort': 8080, 'protocol': 'TCP', 'port': 8080, 'name': 'web3'}]}, 'apiVersion': 'v1', 'metadata': {'name': 'svc-test'}}
after serialization
{'kind': 'Service', 'spec': {'ports': [{'targetPort': 8080, 'protocol': 'TCP', 'port': 8080, 'name': 'web3'}]}, 'apiVersion': 'v1', 'metadata': {'name': 'svc-test'}}
namespace from apply_func
test
Traceback (most recent call last):
File "test.py", line 12, in
resp = dyn_client.apply(service, body=data, namespace="test")
File "/tmp/test/lib/python2.7/site-packages/openshift/dynamic/client.py", line 193, in apply
return apply(resource, body)
File "/tmp/test/lib/python2.7/site-packages/openshift/dynamic/apply.py", line 19, in apply
return resource.create(body=dict_merge(definition, desired_annotation), namespace=definition['metadata'].get('namespace'))
File "/tmp/test/lib/python2.7/site-packages/openshift/dynamic/client.py", line 77, in inner
resp = func(self, resource, *args, **kwargs)
File "/tmp/test/lib/python2.7/site-packages/openshift/dynamic/client.py", line 142, in create
namespace = self.ensure_namespace(resource, namespace, body)
File "/tmp/test/lib/python2.7/site-packages/openshift/dynamic/client.py", line 125, in ensure_namespace
raise ValueError("Namespace is required for {}.{}".format(resource.group_version, resource.kind))
ValueError: Namespace is required for v1.Service

argh, yes, it's obvious now. I had namespace in my test.yaml, no wonder I couldn't reproduce (this is why a full test case with all the data is helpful)

also, please can you enclose your code snippets in triple backticks, it makes them much more readable.

#300 should fix this issue