Bump openshift dep

The openshift library has been completely redesigned with recent
releases so bump the dep and adapt to the new api. The update is
necessary in order to fix a urllib3 version conflict [1].

[1] Trace:
ERROR: nodepool 3.14.1.dev3 has requirement urllib3<1.26,>=1.25.4, but you'll have urllib3 1.24 which is incompatible.
ERROR: kubernetes 8.0.2 has requirement urllib3>=1.24.2, but you'll have urllib3 1.24 which is incompatible.
ERROR: botocore 1.19.30 has requirement urllib3<1.27,>=1.25.4; python_version != "3.4", but you'll have urllib3 1.24 which is incompatible.

Change-Id: Ia4d09fd0a4a49d644bb575b74184de930c62ce89
Co-Authored-By: Tobias Henkel <tobias.henkel@bmw.de>
Story: 2008427
Task: 41373
This commit is contained in:
Clark Boylan 2020-12-07 12:46:20 -08:00 committed by Tobias Henkel
parent d1a123111b
commit e7f831c34e
No known key found for this signature in database
GPG Key ID: 03750DEC158E5FA2
8 changed files with 118 additions and 57 deletions

View File

@ -14,16 +14,21 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from kubernetes import config as k8s_config
from nodepool.driver import Driver from nodepool.driver import Driver
from nodepool.driver.kubernetes.config import KubernetesProviderConfig from nodepool.driver.kubernetes.config import KubernetesProviderConfig
from nodepool.driver.kubernetes.provider import KubernetesProvider from nodepool.driver.kubernetes.provider import KubernetesProvider
from openshift import config
class KubernetesDriver(Driver): class KubernetesDriver(Driver):
def reset(self): def reset(self):
try: try:
config.load_kube_config(persist_config=True) k8s_config.load_kube_config(persist_config=True)
except k8s_config.config_exception.ConfigException as e:
if 'Invalid kube-config file. No configuration found.' in str(e):
pass
else:
raise
except FileNotFoundError: except FileNotFoundError:
pass pass

View File

@ -18,9 +18,8 @@ import math
import urllib3 import urllib3
import time import time
from kubernetes.config import config_exception as kce
from kubernetes import client as k8s_client from kubernetes import client as k8s_client
from openshift import config from kubernetes import config as k8s_config
from nodepool import exceptions from nodepool import exceptions
from nodepool.driver import Provider from nodepool.driver import Provider
@ -41,10 +40,10 @@ class KubernetesProvider(Provider, QuotaSupport):
try: try:
self.k8s_client, self.rbac_client = self._get_client( self.k8s_client, self.rbac_client = self._get_client(
provider.context) provider.context)
except kce.ConfigException: except k8s_config.config_exception.ConfigException:
self.log.exception("Couldn't load client from config") self.log.exception("Couldn't load client from config")
self.log.info("Get context list using this command: " self.log.info("Get context list using this command: "
"python3 -c \"from openshift import config; " "python3 -c \"from kubernetes import config; "
"print('\\n'.join([i['name'] for i in " "print('\\n'.join([i['name'] for i in "
"config.list_kube_config_contexts()[0]]))\"") "config.list_kube_config_contexts()[0]]))\"")
self.k8s_client = None self.k8s_client = None
@ -55,11 +54,11 @@ class KubernetesProvider(Provider, QuotaSupport):
def _get_client(self, context): def _get_client(self, context):
try: try:
conf = config.new_client_from_config(context=context) conf = k8s_config.new_client_from_config(context=context)
except FileNotFoundError: except FileNotFoundError:
self.log.debug("Kubernetes config file not found, attempting " self.log.debug("Kubernetes config file not found, attempting "
"to load in-cluster configs") "to load in-cluster configs")
conf = config.load_incluster_config() conf = k8s_config.load_incluster_config()
return ( return (
k8s_client.CoreV1Api(conf), k8s_client.CoreV1Api(conf),

View File

@ -14,10 +14,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from kubernetes import config as k8s_config
from nodepool.driver import Driver from nodepool.driver import Driver
from nodepool.driver.openshift.config import OpenshiftProviderConfig from nodepool.driver.openshift.config import OpenshiftProviderConfig
from nodepool.driver.openshift.provider import OpenshiftProvider from nodepool.driver.openshift.provider import OpenshiftProvider
from openshift import config
class OpenshiftDriver(Driver): class OpenshiftDriver(Driver):
@ -26,7 +26,12 @@ class OpenshiftDriver(Driver):
def reset(self): def reset(self):
try: try:
config.load_kube_config(persist_config=True) k8s_config.load_kube_config(persist_config=True)
except k8s_config.config_exception.ConfigException as e:
if 'Invalid kube-config file. No configuration found.' in str(e):
pass
else:
raise
except FileNotFoundError: except FileNotFoundError:
pass pass

View File

@ -17,10 +17,9 @@ import logging
import urllib3 import urllib3
import time import time
from kubernetes.config import config_exception as kce
from kubernetes import client as k8s_client from kubernetes import client as k8s_client
from openshift import client as os_client from kubernetes import config as k8s_config
from openshift import config from openshift.dynamic import DynamicClient as os_client
from nodepool import exceptions from nodepool import exceptions
from nodepool.driver import Provider from nodepool.driver import Provider
@ -38,7 +37,7 @@ class OpenshiftProvider(Provider):
try: try:
self.os_client, self.k8s_client = self._get_client( self.os_client, self.k8s_client = self._get_client(
provider.context) provider.context)
except kce.ConfigException: except k8s_config.config_exception.ConfigException:
self.log.exception( self.log.exception(
"Couldn't load context %s from config", provider.context) "Couldn't load context %s from config", provider.context)
self.os_client = None self.os_client = None
@ -48,9 +47,9 @@ class OpenshiftProvider(Provider):
self.project_names.add(pool.name) self.project_names.add(pool.name)
def _get_client(self, context): def _get_client(self, context):
conf = config.new_client_from_config(context=context) conf = k8s_config.new_client_from_config(context=context)
return ( return (
os_client.OapiApi(conf), os_client(conf),
k8s_client.CoreV1Api(conf)) k8s_client.CoreV1Api(conf))
def start(self, zk_conn): def start(self, zk_conn):
@ -87,7 +86,9 @@ class OpenshiftProvider(Provider):
return getattr(self, name, default) return getattr(self, name, default)
if self.ready: if self.ready:
for project in self.os_client.list_project().items: projects = self.os_client.resources.get(api_version='v1',
kind='Project')
for project in projects.get().items:
servers.append(FakeServer( servers.append(FakeServer(
project, self.provider.name, self.project_names)) project, self.provider.name, self.project_names))
return servers return servers
@ -107,16 +108,20 @@ class OpenshiftProvider(Provider):
return return
self.log.debug("%s: removing project" % server_id) self.log.debug("%s: removing project" % server_id)
try: try:
self.os_client.delete_project(server_id) project = self.os_client.resources.get(api_version='v1',
kind='Project')
project.delete(name=server_id)
self.log.info("%s: project removed" % server_id) self.log.info("%s: project removed" % server_id)
except Exception: except Exception:
# TODO: implement better exception handling # TODO: implement better exception handling
self.log.exception("Couldn't remove project %s" % server_id) self.log.exception("Couldn't remove project %s" % server_id)
def waitForNodeCleanup(self, server_id): def waitForNodeCleanup(self, server_id):
project = self.os_client.resources.get(api_version='v1',
kind='Project')
for retry in range(300): for retry in range(300):
try: try:
self.os_client.read_project(server_id) project.get(name=server_id)
except Exception: except Exception:
break break
time.sleep(1) time.sleep(1)
@ -125,13 +130,15 @@ class OpenshiftProvider(Provider):
self.log.debug("%s: creating project" % project) self.log.debug("%s: creating project" % project)
# Create the project # Create the project
proj_body = { proj_body = {
'apiVersion': 'v1', 'apiVersion': 'project.openshift.io/v1',
'kind': 'ProjectRequest', 'kind': 'ProjectRequest',
'metadata': { 'metadata': {
'name': project, 'name': project,
} }
} }
self.os_client.create_project_request(proj_body) projects = self.os_client.resources.get(
api_version='project.openshift.io/v1', kind='ProjectRequest')
projects.create(body=proj_body)
return project return project
def prepareProject(self, project): def prepareProject(self, project):
@ -170,8 +177,8 @@ class OpenshiftProvider(Provider):
(project, sa)) (project, sa))
# Give service account admin access # Give service account admin access
role_body = { role_binding_body = {
'apiVersion': 'v1', 'apiVersion': 'authorization.openshift.io/v1',
'kind': 'RoleBinding', 'kind': 'RoleBinding',
'metadata': {'name': 'admin-0'}, 'metadata': {'name': 'admin-0'},
'roleRef': {'name': 'admin'}, 'roleRef': {'name': 'admin'},
@ -180,18 +187,21 @@ class OpenshiftProvider(Provider):
'name': user, 'name': user,
'namespace': project, 'namespace': project,
}], }],
'userNames': ['system:serviceaccount:%s:zuul-worker' % project] 'userNames': ['system:serviceaccount:%s:%s' % (project, user)]
} }
try: try:
self.os_client.create_namespaced_role_binding(project, role_body) role_bindings = self.os_client.resources.get(
api_version='authorization.openshift.io/v1',
kind='RoleBinding')
role_bindings.create(body=role_binding_body, namespace=project)
except ValueError: except ValueError:
# https://github.com/ansible/ansible/issues/36939 # https://github.com/ansible/ansible/issues/36939
pass pass
resource = { resource = {
'namespace': project, 'namespace': project,
'host': self.os_client.api_client.configuration.host, 'host': self.os_client.configuration.host,
'skiptls': not self.os_client.api_client.configuration.verify_ssl, 'skiptls': not self.os_client.configuration.verify_ssl,
'token': token, 'token': token,
'user': user, 'user': user,
} }

View File

@ -14,10 +14,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from kubernetes import config as k8s_config
from nodepool.driver import Driver from nodepool.driver import Driver
from nodepool.driver.openshiftpods.config import OpenshiftPodsProviderConfig from nodepool.driver.openshiftpods.config import OpenshiftPodsProviderConfig
from nodepool.driver.openshiftpods.provider import OpenshiftPodsProvider from nodepool.driver.openshiftpods.provider import OpenshiftPodsProvider
from openshift import config
class OpenshiftPodsDriver(Driver): class OpenshiftPodsDriver(Driver):
@ -26,7 +26,12 @@ class OpenshiftPodsDriver(Driver):
def reset(self): def reset(self):
try: try:
config.load_kube_config(persist_config=True) k8s_config.load_kube_config(persist_config=True)
except k8s_config.config_exception.ConfigException as e:
if 'Invalid kube-config file. No configuration found.' in str(e):
pass
else:
raise
except FileNotFoundError: except FileNotFoundError:
pass pass

View File

@ -17,9 +17,8 @@ import logging
import urllib3 import urllib3
import time import time
from kubernetes.config import config_exception as kce
from kubernetes import client as k8s_client from kubernetes import client as k8s_client
from openshift import config from kubernetes import config as k8s_config
from nodepool.driver.openshift.provider import OpenshiftProvider from nodepool.driver.openshift.provider import OpenshiftProvider
from nodepool.driver.openshiftpods import handler from nodepool.driver.openshiftpods import handler
@ -37,10 +36,10 @@ class OpenshiftPodsProvider(OpenshiftProvider):
try: try:
self.token, self.ca_crt, self.k8s_client = self._get_client( self.token, self.ca_crt, self.k8s_client = self._get_client(
provider.context) provider.context)
except kce.ConfigException: except k8s_config.config_exception.ConfigException:
self.log.exception("Couldn't load client from config") self.log.exception("Couldn't load client from config")
self.log.info("Get context list using this command: " self.log.info("Get context list using this command: "
"python3 -c \"from openshift import config; " "python3 -c \"from kubernetes import config; "
"print('\\n'.join([i['name'] for i in " "print('\\n'.join([i['name'] for i in "
"config.list_kube_config_contexts()[0]]))\"") "config.list_kube_config_contexts()[0]]))\"")
self.token = None self.token = None
@ -51,7 +50,7 @@ class OpenshiftPodsProvider(OpenshiftProvider):
self.pod_names.update(pool.labels.keys()) self.pod_names.update(pool.labels.keys())
def _get_client(self, context): def _get_client(self, context):
conf = config.new_client_from_config(context=context) conf = k8s_config.new_client_from_config(context=context)
token = conf.configuration.api_key.get('authorization', '').split()[-1] token = conf.configuration.api_key.get('authorization', '').split()[-1]
ca = None ca = None
if conf.configuration.ssl_ca_cert: if conf.configuration.ssl_ca_cert:

View File

@ -21,42 +21,77 @@ from nodepool import zk
from nodepool.driver.openshift import provider from nodepool.driver.openshift import provider
class FakeOpenshiftClient(object): class FakeOpenshiftProjectsQuery:
def __init__(self):
self.projects = []
class FakeApi: def __init__(self, client):
class configuration: self.client = client
host = "http://localhost:8080"
verify_ssl = False
self.api_client = FakeApi()
def list_project(self): def get(self):
class FakeProjects: class FakeProjectsResult:
items = self.projects def __init__(self, items):
return FakeProjects self.items = items
def create_project_request(self, proj_body): return FakeProjectsResult(self.client.projects)
class FakeProject:
class metadata:
name = proj_body['metadata']['name']
self.projects.append(FakeProject)
return FakeProject
def delete_project(self, name): def delete(self, name):
to_delete = None to_delete = None
for project in self.projects: for project in self.client.projects:
if project.metadata.name == name: if project.metadata.name == name:
to_delete = project to_delete = project
break break
if not to_delete: if not to_delete:
raise RuntimeError("Unknown project %s" % name) raise RuntimeError("Unknown project %s" % name)
self.projects.remove(to_delete) self.client.projects.remove(to_delete)
def create_namespaced_role_binding(self, ns, role_binding_body):
class FakeOpenshiftProjectRequestQuery:
def __init__(self, client):
self.client = client
def create(self, body):
class FakeProject:
class metadata:
name = body['metadata']['name']
self.client.projects.append(FakeProject)
return FakeProject
class FakeOpenshiftRoleBindingQuery:
def __init__(self, client):
self.client = client
def create(self, body, namespace):
return return
class FakeOpenshiftResources:
def __init__(self, client):
self.client = client
def get(self, api_version=None, kind=None):
if kind == 'Project':
return FakeOpenshiftProjectsQuery(self.client)
if kind == 'ProjectRequest':
return FakeOpenshiftProjectRequestQuery(self.client)
if kind == 'RoleBinding':
return FakeOpenshiftRoleBindingQuery(self.client)
raise NotImplementedError
class FakeOpenshiftClient(object):
def __init__(self):
self.projects = []
class FakeConfiguration:
host = "http://localhost:8080"
verify_ssl = False
self.configuration = FakeConfiguration()
self.resources = FakeOpenshiftResources(self)
class FakeCoreClient(object): class FakeCoreClient(object):
def create_namespaced_service_account(self, ns, sa_body): def create_namespaced_service_account(self, ns, sa_body):
return return
@ -153,3 +188,6 @@ class TestDriverOpenshift(tests.DBTestCase):
self.zk.storeNode(node) self.zk.storeNode(node)
self.waitForNodeDeletion(node) self.waitForNodeDeletion(node)
self.assertEqual(len(self.fake_os_client.projects), 0,
'Project must be cleaned up')

View File

@ -15,7 +15,7 @@ voluptuous
kazoo>=2.8.0 kazoo>=2.8.0
Paste Paste
WebOb>=1.8.1 WebOb>=1.8.1
openshift<=0.8.9 openshift<0.12.0
boto3 boto3
google-api-python-client google-api-python-client
azure-mgmt-compute<17.0.0 azure-mgmt-compute<17.0.0