Browse Source

Add extra debug info to csr module

This commit ensures we capture even more data in case this
module fails to approve csrs for some reason.  This will
greatly aid in troubleshooting from users.
Michael Gugino 6 years ago
parent
commit
caba6bc1db
2 changed files with 255 additions and 239 deletions
  1. 226 208
      roles/lib_openshift/library/oc_csr_approve.py
  2. 29 31
      roles/lib_openshift/test/test_oc_csr_approve.py

+ 226 - 208
roles/lib_openshift/library/oc_csr_approve.py

@@ -53,60 +53,6 @@ EXAMPLES = '''
 CERT_MODE = {'client': 'client auth', 'server': 'server auth'}
 
 
-def run_command(module, command, rc_opts=None):
-    '''Run a command using AnsibleModule.run_command, or fail'''
-    if rc_opts is None:
-        rc_opts = {}
-    rtnc, stdout, err = module.run_command(command, **rc_opts)
-    if rtnc:
-        result = {'failed': True,
-                  'changed': False,
-                  'msg': str(err),
-                  'state': 'unknown'}
-        module.fail_json(**result)
-    return stdout
-
-
-def get_ready_nodes(module, oc_bin, oc_conf):
-    '''Get list of nodes currently ready vi oc'''
-    # json output is necessary for consistency here.
-    command = "{} {} get nodes -ojson".format(oc_bin, oc_conf)
-    stdout = run_command(module, command)
-
-    try:
-        data = json.loads(stdout)
-    except JSONDecodeError as err:
-        result = {'failed': True,
-                  'changed': False,
-                  'msg': str(err),
-                  'state': 'unknown'}
-        module.fail_json(**result)
-
-    ready_nodes = []
-    for node in data['items']:
-        if node.get('status') and node['status'].get('conditions'):
-            for condition in node['status']['conditions']:
-                # "True" is a string here, not a boolean.
-                if condition['type'] == "Ready" and condition['status'] == 'True':
-                    ready_nodes.append(node['metadata']['name'])
-    return ready_nodes
-
-
-def get_csrs(module, oc_bin, oc_conf):
-    '''Retrieve csrs from cluster using oc get csr -ojson'''
-    command = "{} {} get csr -ojson".format(oc_bin, oc_conf)
-    stdout = run_command(module, command)
-    try:
-        data = json.loads(stdout)
-    except JSONDecodeError as err:
-        result = {'failed': True,
-                  'changed': False,
-                  'msg': str(err),
-                  'state': 'unknown'}
-        module.fail_json(**result)
-    return data['items']
-
-
 def parse_subject_cn(subject_str):
     '''parse output of openssl req -noout -subject to retrieve CN.
        example input:
@@ -125,114 +71,231 @@ def parse_subject_cn(subject_str):
             return item_parts[1]
 
 
-def process_csrs(module, csrs, node_list, mode):
-    '''Return a dictionary of pending csrs where the format of the dict is
-       k=csr name, v=Subject Common Name'''
-    csr_dict = {}
-    for item in csrs:
-        status = item['status'].get('conditions')
-        if status:
-            # If status is not an empty dictionary, cert is not pending.
-            continue
-        if CERT_MODE[mode] not in item['spec']['usages']:
-            continue
-        name = item['metadata']['name']
-        request_data = base64.b64decode(item['spec']['request'])
-        command = "openssl req -noout -subject"
-        # ansible's module.run_command accepts data to pipe via stdin as
-        # as 'data' kwarg.
-        rc_opts = {'data': request_data, 'binary_data': True}
-        stdout = run_command(module, command, rc_opts=rc_opts)
-        # parse common_name from subject string.
-        common_name = parse_subject_cn(stdout)
-        if common_name and common_name.startswith('system:node:'):
-            # common name is typically prepended with system:node:.
-            common_name = common_name.split('system:node:')[1]
-        # we only want to approve csrs from nodes we know about.
-        if common_name in node_list:
-            csr_dict[name] = common_name
-
-    return csr_dict
-
-
-def confirm_needed_requests_present(module, not_ready_nodes, csr_dict):
-    '''Ensure all non-Ready nodes have a csr, or fail'''
-    nodes_needed = set(not_ready_nodes)
-    for _, val in csr_dict.items():
-        nodes_needed.discard(val)
-
-    # check that we found all of our needed nodes
-    if nodes_needed:
-        missing_nodes = ', '.join(nodes_needed)
-        result = {'failed': True,
-                  'changed': False,
-                  'msg': "Could not find csr for nodes: {}".format(missing_nodes),
-                  'state': 'unknown'}
-        module.fail_json(**result)
-
-
-def approve_csrs(module, oc_bin, oc_conf, csr_pending_list, mode):
-    '''Loop through csr_pending_list and call:
-       oc adm certificate approve <item>'''
-    res_mode = "{}_approve_results".format(mode)
-    base_command = "{} {} adm certificate approve {}"
-    approve_results = []
-    for csr in csr_pending_list:
-        command = base_command.format(oc_bin, oc_conf, csr)
-        rtnc, stdout, err = module.run_command(command)
-        approve_results.append(stdout)
+class CSRapprove(object):
+    """Approves csr requests"""
+
+    def __init__(self, module, oc_bin, oc_conf, node_list):
+        '''init method'''
+        self.module = module
+        self.oc_bin = oc_bin
+        self.oc_conf = oc_conf
+        self.node_list = node_list
+        self.all_subjects_found = []
+        self.unwanted_csrs = []
+        # Build a dictionary to hold all of our output information so nothing
+        # is lost when we fail.
+        self.result = {'changed': False, 'rc': 0,
+                       'client_csrs': None,
+                       'server_csrs': None,
+                       'all_subjects_found': self.all_subjects_found,
+                       'client_approve_results': [],
+                       'server_approve_results': [],
+                       'unwanted_csrs': self.unwanted_csrs}
+
+    def run_command(self, command, rc_opts=None):
+        '''Run a command using AnsibleModule.run_command, or fail'''
+        if rc_opts is None:
+            rc_opts = {}
+        rtnc, stdout, err = self.module.run_command(command, **rc_opts)
         if rtnc:
-            result = {'failed': True,
-                      'changed': False,
-                      'msg': str(err),
-                      res_mode: approve_results,
-                      'state': 'unknown'}
-            module.fail_json(**result)
-    return approve_results
-
-
-def get_ready_nodes_server(module, oc_bin, oc_conf, nodes_list):
-    '''Determine which nodes have working server certificates'''
-    ready_nodes_server = []
-    base_command = "{} {} get --raw /api/v1/nodes/{}/proxy/healthz"
-    for node in nodes_list:
-        # need this to look like /api/v1/nodes/<node>/proxy/healthz
-        command = base_command.format(oc_bin, oc_conf, node)
-        rtnc, _, _ = module.run_command(command)
-        if not rtnc:
-            # if we can hit that api endpoint, the node has a valid server
-            # cert.
-            ready_nodes_server.append(node)
-    return ready_nodes_server
-
-
-def verify_server_csrs(module, result, oc_bin, oc_conf, node_list):
-    '''We approved some server csrs, now we need to validate they are working.
-       This function will attempt to retry 10 times in case of failure.'''
-    # Attempt to try node endpoints a few times.
-    attempts = 0
-    # Find not_ready_nodes for server-side again
-    nodes_server_ready = get_ready_nodes_server(module, oc_bin, oc_conf,
-                                                node_list)
-    # Create list of nodes that still aren't ready.
-    not_ready_nodes_server = set([item for item in node_list if item not in nodes_server_ready])
-    while not_ready_nodes_server:
-        nodes_server_ready = get_ready_nodes_server(module, oc_bin, oc_conf,
-                                                    not_ready_nodes_server)
-
-        # if we have same number of nodes_server_ready now, all of the previous
-        # not_ready_nodes are now ready.
-        if not len(not_ready_nodes_server - set(nodes_server_ready)):
-            break
-        attempts += 1
-        if attempts > 9:
-            result['failed'] = True
-            result['rc'] = 1
-            missing_nodes = not_ready_nodes_server - set(nodes_server_ready)
-            msg = "Some nodes still not ready after approving server certs: {}"
-            msg = msg.format(", ".join(missing_nodes))
-            result['msg'] = msg
-            module.fail_json(**result)
+            self.result['failed'] = True
+            self.result['msg'] = str(err)
+            self.result['state'] = 'unknown'
+            self.module.fail_json(**self.result)
+        return stdout
+
+    def get_ready_nodes(self):
+        '''Get list of nodes currently ready vi oc'''
+        # json output is necessary for consistency here.
+        command = "{} {} get nodes -ojson".format(self.oc_bin, self.oc_conf)
+        stdout = self.run_command(command)
+
+        try:
+            data = json.loads(stdout)
+        except JSONDecodeError as err:
+            self.result['failed'] = True
+            self.result['msg'] = str(err)
+            self.result['state'] = 'unknown'
+            self.module.fail_json(**self.result)
+
+        ready_nodes = []
+        for node in data['items']:
+            if node.get('status') and node['status'].get('conditions'):
+                for condition in node['status']['conditions']:
+                    # "True" is a string here, not a boolean.
+                    if condition['type'] == "Ready" and condition['status'] == 'True':
+                        ready_nodes.append(node['metadata']['name'])
+        return ready_nodes
+
+    def get_csrs(self):
+        '''Retrieve csrs from cluster using oc get csr -ojson'''
+        command = "{} {} get csr -ojson".format(self.oc_bin, self.oc_conf)
+        stdout = self.run_command(command)
+        try:
+            data = json.loads(stdout)
+        except JSONDecodeError as err:
+            self.result['failed'] = True
+            self.result['msg'] = str(err)
+            self.result['state'] = 'unknown'
+            self.module.fail_json(**self.result)
+        return data['items']
+
+    def process_csrs(self, csrs, mode):
+        '''Return a dictionary of pending csrs where the format of the dict is
+           k=csr name, v=Subject Common Name'''
+        csr_dict = {}
+        for item in csrs:
+            name = item['metadata']['name']
+            request_data = base64.b64decode(item['spec']['request'])
+            command = "openssl req -noout -subject"
+            # ansible's module.run_command accepts data to pipe via stdin as
+            # as 'data' kwarg.
+            rc_opts = {'data': request_data, 'binary_data': True}
+            stdout = self.run_command(command, rc_opts=rc_opts)
+            self.all_subjects_found.append(stdout)
+
+            status = item['status'].get('conditions')
+            if status:
+                # If status is not an empty dictionary, cert is not pending.
+                self.unwanted_csrs.append(item)
+                continue
+            if CERT_MODE[mode] not in item['spec']['usages']:
+                self.unwanted_csrs.append(item)
+                continue
+            # parse common_name from subject string.
+            common_name = parse_subject_cn(stdout)
+            if common_name and common_name.startswith('system:node:'):
+                # common name is typically prepended with system:node:.
+                common_name = common_name.split('system:node:')[1]
+            # we only want to approve csrs from nodes we know about.
+            if common_name in self.node_list:
+                csr_dict[name] = common_name
+            else:
+                self.unwanted_csrs.append(item)
+
+        return csr_dict
+
+    def confirm_needed_requests_present(self, not_ready_nodes, csr_dict):
+        '''Ensure all non-Ready nodes have a csr, or fail'''
+        nodes_needed = set(not_ready_nodes)
+        for _, val in csr_dict.items():
+            nodes_needed.discard(val)
+
+        # check that we found all of our needed nodes
+        if nodes_needed:
+            missing_nodes = ', '.join(nodes_needed)
+            self.result['failed'] = True
+            self.result['msg'] = "Could not find csr for nodes: {}".format(missing_nodes)
+            self.result['state'] = 'unknown'
+            self.module.fail_json(**self.result)
+
+    def approve_csrs(self, csr_pending_list, mode):
+        '''Loop through csr_pending_list and call:
+           oc adm certificate approve <item>'''
+        res_mode = "{}_approve_results".format(mode)
+        base_command = "{} {} adm certificate approve {}"
+        approve_results = []
+        for csr in csr_pending_list:
+            command = base_command.format(self.oc_bin, self.oc_conf, csr)
+            rtnc, stdout, err = self.module.run_command(command)
+            approve_results.append(stdout)
+            if rtnc:
+                self.result['failed'] = True
+                self.result['msg'] = str(err)
+                self.result[res_mode] = approve_results
+                self.result['state'] = 'unknown'
+                self.module.fail_json(**self.result)
+        self.result[res_mode] = approve_results
+        # We set changed for approved client or server csrs.
+        self.result['changed'] = bool(approve_results) or bool(self.result['changed'])
+
+    def get_ready_nodes_server(self, nodes_list):
+        '''Determine which nodes have working server certificates'''
+        ready_nodes_server = []
+        base_command = "{} {} get --raw /api/v1/nodes/{}/proxy/healthz"
+        for node in nodes_list:
+            # need this to look like /api/v1/nodes/<node>/proxy/healthz
+            command = base_command.format(self.oc_bin, self.oc_conf, node)
+            rtnc, _, _ = self.module.run_command(command)
+            if not rtnc:
+                # if we can hit that api endpoint, the node has a valid server
+                # cert.
+                ready_nodes_server.append(node)
+        return ready_nodes_server
+
+    def verify_server_csrs(self):
+        '''We approved some server csrs, now we need to validate they are working.
+           This function will attempt to retry 10 times in case of failure.'''
+        # Attempt to try node endpoints a few times.
+        attempts = 0
+        # Find not_ready_nodes for server-side again
+        nodes_server_ready = self.get_ready_nodes_server(self.node_list)
+        # Create list of nodes that still aren't ready.
+        not_ready_nodes_server = set([item for item in self.node_list if item not in nodes_server_ready])
+        while not_ready_nodes_server:
+            nodes_server_ready = self.get_ready_nodes_server(not_ready_nodes_server)
+
+            # if we have same number of nodes_server_ready now, all of the previous
+            # not_ready_nodes are now ready.
+            if not len(not_ready_nodes_server - set(nodes_server_ready)):
+                break
+            attempts += 1
+            if attempts > 9:
+                self.result['failed'] = True
+                self.result['rc'] = 1
+                missing_nodes = not_ready_nodes_server - set(nodes_server_ready)
+                msg = "Some nodes still not ready after approving server certs: {}"
+                msg = msg.format(", ".join(missing_nodes))
+                self.result['msg'] = msg
+                self.module.fail_json(**self.result)
+
+    def run(self):
+        '''execute the csr approval process'''
+        nodes_ready = self.get_ready_nodes()
+        # don't need to check nodes that are already ready.
+        client_not_ready_nodes = [item for item in self.node_list
+                                  if item not in nodes_ready]
+
+        # Get all csrs, no good way to filter on pending.
+        client_csrs = self.get_csrs()
+        # process data in csrs and build a dictionary of client requests
+        client_csr_dict = self.process_csrs(client_csrs, "client")
+        self.result['client_csrs'] = client_csr_dict
+
+        # This method is fail-happy and expects all non-Ready nodes have available
+        # csrs.  Handle failure for this method via ansible retry/until.
+        self.confirm_needed_requests_present(client_not_ready_nodes,
+                                             client_csr_dict)
+
+        self.approve_csrs(client_csr_dict, 'client')
+
+        # # Server Cert Section # #
+        # Find not_ready_nodes for server-side
+        nodes_server_ready = self.get_ready_nodes_server(self.node_list)
+        # Create list of nodes that definitely need a server cert approved.
+        not_ready_nodes_server = [item for item in self.node_list
+                                  if item not in nodes_server_ready]
+
+        # Get all csrs again, no good way to filter on pending.
+        server_csrs = self.get_csrs()
+        # process data in csrs and build a dictionary of server requests
+        server_csr_dict = self.process_csrs(server_csrs, "server")
+        self.result['server_csrs'] = server_csr_dict
+
+        # This will fail if all server csrs are not present, but probably shouldn't
+        # at this point since we spent some time hitting the api to see if the
+        # nodes are already responding.
+        self.confirm_needed_requests_present(not_ready_nodes_server,
+                                             server_csr_dict)
+        self.approve_csrs(server_csr_dict, 'server')
+
+        self.verify_server_csrs()
+
+        # We made it here, everything was successful, cleanup some debug info
+        # so we don't spam logs.
+        for key in ('client_csrs', 'server_csrs', 'unwanted_csrs'):
+            self.result.pop(key)
+        self.module.exit_json(**self.result)
 
 
 def run_module():
@@ -250,53 +313,8 @@ def run_module():
     oc_conf = '--config={}'.format(module.params['oc_conf'])
     node_list = module.params['node_list']
 
-    result = {'changed': False, 'rc': 0}
-
-    nodes_ready = get_ready_nodes(module, oc_bin, oc_conf)
-    # don't need to check nodes that are already ready.
-    not_ready_nodes = [item for item in node_list if item not in nodes_ready]
-
-    # Get all csrs, no good way to filter on pending.
-    csrs = get_csrs(module, oc_bin, oc_conf)
-
-    # process data in csrs and build a dictionary of client requests
-    csr_dict = process_csrs(module, csrs, node_list, "client")
-
-    # This method is fail-happy and expects all non-Ready nodes have available
-    # csrs.  Handle failure for this method via ansible retry/until.
-    confirm_needed_requests_present(module, not_ready_nodes, csr_dict)
-
-    # save client_approve_results so we can report later.
-    client_approve_results = approve_csrs(module, oc_bin, oc_conf, csr_dict,
-                                          'client')
-    result['client_approve_results'] = client_approve_results
-
-    # # Server Cert Section # #
-    # Find not_ready_nodes for server-side
-    nodes_server_ready = get_ready_nodes_server(module, oc_bin, oc_conf,
-                                                node_list)
-    # Create list of nodes that definitely need a server cert approved.
-    not_ready_nodes_server = [item for item in node_list if item not in nodes_server_ready]
-
-    # Get all csrs again, no good way to filter on pending.
-    csrs = get_csrs(module, oc_bin, oc_conf)
-
-    # process data in csrs and build a dictionary of server requests
-    csr_dict = process_csrs(module, csrs, node_list, "server")
-
-    # This will fail if all server csrs are not present, but probably shouldn't
-    # at this point since we spent some time hitting the api to see if the
-    # nodes are already responding.
-    confirm_needed_requests_present(module, not_ready_nodes_server, csr_dict)
-    server_approve_results = approve_csrs(module, oc_bin, oc_conf, csr_dict,
-                                          'server')
-    result['server_approve_results'] = server_approve_results
-
-    result['changed'] = bool(client_approve_results) or bool(server_approve_results)
-
-    verify_server_csrs(module, result, oc_bin, oc_conf, node_list)
-
-    module.exit_json(**result)
+    approver = CSRapprove(module, oc_bin, oc_conf, node_list)
+    approver.run()
 
 
 def main():

+ 29 - 31
roles/lib_openshift/test/test_oc_csr_approve.py

@@ -16,6 +16,7 @@ MODULE_PATH = os.path.realpath(os.path.join(__file__, os.pardir, os.pardir, 'lib
 sys.path.insert(1, MODULE_PATH)
 
 import oc_csr_approve  # noqa
+from oc_csr_approve import CSRapprove # noqa
 
 # base path for text files with sample outputs.
 ASSET_PATH = os.path.realpath(os.path.join(__file__, os.pardir, 'test_data'))
@@ -48,15 +49,17 @@ def test_get_ready_nodes():
         oc_get_nodes_stdout = stdoutfile.read()
 
     module = DummyModule({})
+    approver = CSRapprove(module, 'oc', '/dev/null', [])
 
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, oc_get_nodes_stdout, '')
-        ready_nodes = oc_csr_approve.get_ready_nodes(module, 'oc', '/dev/null')
+        ready_nodes = approver.get_ready_nodes()
     assert ready_nodes == ['fedora1.openshift.io', 'fedora3.openshift.io']
 
 
 def test_get_csrs():
     module = DummyModule({})
+    approver = CSRapprove(module, 'oc', '/dev/null', [])
     output_file = os.path.join(ASSET_PATH, 'oc_csr_approve_pending.json')
     with open(output_file) as stdoutfile:
         oc_get_csr_out = stdoutfile.read()
@@ -64,7 +67,7 @@ def test_get_csrs():
     # mock oc get csr call to cluster
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, oc_get_csr_out, '')
-        csrs = oc_csr_approve.get_csrs(module, 'oc', '/dev/null')
+        csrs = approver.get_csrs()
 
     assert csrs[0]['kind'] == "CertificateSigningRequest"
 
@@ -74,11 +77,12 @@ def test_get_csrs():
 
     # mock openssl req call.
     node_list = ['fedora2.mguginolocal.com']
+    approver = CSRapprove(module, 'oc', '/dev/null', node_list)
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, openssl_out, '')
-        csr_dict = oc_csr_approve.process_csrs(module, csrs, node_list, "client")
+        csr_dict = approver.process_csrs(csrs, "client")
     # actually run openssl req call.
-    csr_dict = oc_csr_approve.process_csrs(module, csrs, node_list, "client")
+    csr_dict = approver.process_csrs(csrs, "client")
     assert csr_dict['node-csr-TkefytQp8Dz4Xp7uzcw605MocvI0gWuEOGNrHhOjGNQ'] == 'fedora2.mguginolocal.com'
 
 
@@ -86,38 +90,33 @@ def test_confirm_needed_requests_present():
     module = DummyModule({})
     csr_dict = {'some-csr': 'fedora1.openshift.io'}
     not_ready_nodes = ['host1']
+    approver = CSRapprove(module, 'oc', '/dev/null', [])
     with pytest.raises(Exception) as err:
-        oc_csr_approve.confirm_needed_requests_present(
-            module, not_ready_nodes, csr_dict)
-    assert 'Exception: Could not find csr for nodes: host1' in str(err)
+        approver.confirm_needed_requests_present(not_ready_nodes, csr_dict)
+        assert 'Exception: Could not find csr for nodes: host1' in str(err)
 
     not_ready_nodes = ['fedora1.openshift.io']
     # this should complete silently
-    oc_csr_approve.confirm_needed_requests_present(
-        module, not_ready_nodes, csr_dict)
+    approver.confirm_needed_requests_present(not_ready_nodes, csr_dict)
 
 
 def test_approve_csrs():
     module = DummyModule({})
-    oc_bin = 'oc'
-    oc_conf = '/dev/null'
     csr_dict = {'csr-1': 'example.openshift.io'}
+    approver = CSRapprove(module, 'oc', '/dev/null', [])
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, 'csr-1 ok', '')
-        client_approve_results = oc_csr_approve.approve_csrs(
-            module, oc_bin, oc_conf, csr_dict, 'client')
-    assert client_approve_results == ['csr-1 ok']
+        approver.approve_csrs(csr_dict, 'client')
+    assert approver.result['client_approve_results'] == ['csr-1 ok']
 
 
 def test_get_ready_nodes_server():
     module = DummyModule({})
-    oc_bin = 'oc'
-    oc_conf = '/dev/null'
     nodes_list = ['fedora1.openshift.io']
+    approver = CSRapprove(module, 'oc', '/dev/null', nodes_list)
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, 'ok', '')
-        ready_nodes_server = oc_csr_approve.get_ready_nodes_server(
-            module, oc_bin, oc_conf, nodes_list)
+        ready_nodes_server = approver.get_ready_nodes_server(nodes_list)
     assert ready_nodes_server == ['fedora1.openshift.io']
 
 
@@ -127,10 +126,11 @@ def test_get_csrs_server():
     with open(output_file) as stdoutfile:
         oc_get_csr_out = stdoutfile.read()
 
+    approver = CSRapprove(module, 'oc', '/dev/null', [])
     # mock oc get csr call to cluster
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, oc_get_csr_out, '')
-        csrs = oc_csr_approve.get_csrs(module, 'oc', '/dev/null')
+        csrs = approver.get_csrs()
 
     assert csrs[0]['kind'] == "CertificateSigningRequest"
 
@@ -139,37 +139,35 @@ def test_get_csrs_server():
         openssl_out = stdoutfile.read()
 
     node_list = ['fedora1.openshift.io']
-
+    approver = CSRapprove(module, 'oc', '/dev/null', node_list)
     # mock openssl req call.
     with patch(RUN_CMD_MOCK) as call_mock:
         call_mock.return_value = (0, openssl_out, '')
-        csr_dict = oc_csr_approve.process_csrs(module, csrs, node_list, "server")
+        csr_dict = approver.process_csrs(csrs, "server")
 
     # actually run openssl req call.
     node_list = ['fedora2.mguginolocal.com']
-    csr_dict = oc_csr_approve.process_csrs(module, csrs, node_list, "server")
+    approver = CSRapprove(module, 'oc', '/dev/null', node_list)
+    csr_dict = approver.process_csrs(csrs, "server")
     assert csr_dict['csr-2cxkp'] == 'fedora2.mguginolocal.com'
 
 
 def test_verify_server_csrs():
     module = DummyModule({})
-    oc_bin = 'oc'
-    oc_conf = '/dev/null'
-    result = {}
     ready_nodes_server = ['fedora1.openshift.io']
     node_list = ['fedora1.openshift.io']
-    with patch('oc_csr_approve.get_ready_nodes_server') as call_mock:
+    approver = CSRapprove(module, 'oc', '/dev/null', node_list)
+    with patch('oc_csr_approve.CSRapprove.get_ready_nodes_server') as call_mock:
         call_mock.return_value = ready_nodes_server
         # This should silently return
-        oc_csr_approve.verify_server_csrs(module, result, oc_bin, oc_conf,
-                                          node_list)
+        approver.verify_server_csrs()
 
     node_list = ['fedora1.openshift.io', 'fedora2.openshift.io']
-    with patch('oc_csr_approve.get_ready_nodes_server') as call_mock:
+    approver = CSRapprove(module, 'oc', '/dev/null', node_list)
+    with patch('oc_csr_approve.CSRapprove.get_ready_nodes_server') as call_mock:
         call_mock.return_value = ready_nodes_server
         with pytest.raises(Exception) as err:
-            oc_csr_approve.verify_server_csrs(module, result, oc_bin, oc_conf,
-                                              node_list)
+            approver.verify_server_csrs()
         assert 'after approving server certs: fedora2.openshift.io' in str(err)