From dab80e805c1e8913974693e73a1926ea53882878 Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Wed, 11 Jul 2018 16:50:50 -0400 Subject: [PATCH 1/3] Fix state.sls_id to run on ssh minion and not master --- salt/client/ssh/wrapper/state.py | 92 +++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/salt/client/ssh/wrapper/state.py b/salt/client/ssh/wrapper/state.py index 00539e89e7..2a7e4aa130 100644 --- a/salt/client/ssh/wrapper/state.py +++ b/salt/client/ssh/wrapper/state.py @@ -31,6 +31,56 @@ __func_alias__ = { } log = logging.getLogger(__name__) +def _ssh_state(chunks, __opts__, __context__, __pillar__, __salt__, st_kwargs, + kwargs, test=False): + file_refs = salt.client.ssh.state.lowstate_file_refs( + chunks, + _merge_extra_filerefs( + kwargs.get('extra_filerefs', ''), + __opts__.get('extra_filerefs', '') + ) + ) + # Create the tar containing the state pkg and relevant files. + trans_tar = salt.client.ssh.state.prep_trans_tar( + __opts__, + __context__['fileclient'], + chunks, + file_refs, + __pillar__, + st_kwargs['id_']) + trans_tar_sum = salt.utils.get_hash(trans_tar, __opts__['hash_type']) + cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format( + __opts__['thin_dir'], + test, + trans_tar_sum, + __opts__['hash_type']) + single = salt.client.ssh.Single( + __opts__, + cmd, + fsclient=__context__['fileclient'], + minion_opts=__salt__.minion_opts, + **st_kwargs) + single.shell.send( + trans_tar, + '{0}/salt_state.tgz'.format(__opts__['thin_dir'])) + stdout, stderr, _ = single.cmd_block() + + # Clean up our tar + try: + os.remove(trans_tar) + except (OSError, IOError): + pass + + # Read in the JSON data and return the data structure + try: + return json.loads(stdout, object_hook=salt.utils.decode_dict) + except Exception as e: + log.error("JSON Render failed for: {0}\n{1}".format(stdout, stderr)) + log.error(str(e)) + + # If for some reason the json load fails, return the stdout + return stdout + def _set_retcode(ret, highstate=None): ''' @@ -835,6 +885,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): salt '*' state.sls_id my_state my_module,a_common_module ''' + st_kwargs = __salt__.kwargs conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict @@ -847,13 +898,11 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): if opts['environment'] is None: opts['environment'] = 'base' - try: - st_ = salt.state.HighState(opts, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) - except NameError: - st_ = salt.state.HighState(opts, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.client.ssh.state.SSHHighState( + __opts__, + __pillar__, + __salt__, + __context__['fileclient']) if not _check_pillar(kwargs, st_.opts['pillar']): __context__['retcode'] = 5 @@ -864,10 +913,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): if isinstance(mods, six.string_types): split_mods = mods.split(',') st_.push_active() - try: - high_, errors = st_.render_highstate({opts['environment']: split_mods}) - finally: - st_.pop_active() + high_, errors = st_.render_highstate({opts['environment']: split_mods}) errors += st_.state.verify_high(high_) # Apply requisites to high data high_, req_in_errors = st_.state.requisite_in(high_) @@ -879,20 +925,26 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): __context__['retcode'] = 1 return errors chunks = st_.state.compile_high_data(high_) - ret = {} - for chunk in chunks: - if chunk.get('__id__', '') == id_: - ret.update(st_.state.call_chunk(chunk, {}, chunks)) + chunk = [x for x in chunks if x.get('__id__', '') == id_] - _set_retcode(ret, highstate=highstate) - # Work around Windows multiprocessing bug, set __opts__['test'] back to - # value from before this function was run. - __opts__['test'] = orig_test - if not ret: + if not chunk: raise SaltInvocationError( 'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv ' '\'{2}\''.format(id_, mods, opts['environment']) ) + + ret = _ssh_state(chunk, + __opts__, + __context__, + __pillar__, + __salt__, + st_kwargs, + kwargs, + test=test) + _set_retcode(ret, highstate=highstate) + # Work around Windows multiprocessing bug, set __opts__['test'] back to + # value from before this function was run. + __opts__['test'] = orig_test return ret From 70e36764ee2575a86bfc8c0098b37443f4ec7f53 Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Thu, 12 Jul 2018 12:16:33 -0400 Subject: [PATCH 2/3] Add more tests for salt-ssh state.sls_id --- salt/client/ssh/wrapper/state.py | 3 +++ .../files/file/base/ssh_state_tests.sls | 4 +++ tests/integration/ssh/test_state.py | 27 +++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/salt/client/ssh/wrapper/state.py b/salt/client/ssh/wrapper/state.py index 2a7e4aa130..6b8f39325e 100644 --- a/salt/client/ssh/wrapper/state.py +++ b/salt/client/ssh/wrapper/state.py @@ -33,6 +33,9 @@ log = logging.getLogger(__name__) def _ssh_state(chunks, __opts__, __context__, __pillar__, __salt__, st_kwargs, kwargs, test=False): + ''' + funciton to run a state with the given chunk via salt-ssh + ''' file_refs = salt.client.ssh.state.lowstate_file_refs( chunks, _merge_extra_filerefs( diff --git a/tests/integration/files/file/base/ssh_state_tests.sls b/tests/integration/files/file/base/ssh_state_tests.sls index a121bdfb77..642d33fa3c 100644 --- a/tests/integration/files/file/base/ssh_state_tests.sls +++ b/tests/integration/files/file/base/ssh_state_tests.sls @@ -3,3 +3,7 @@ ssh-file-test: file.managed: - name: /tmp/{{ jinja }} - contents: 'test' + +second_id: + cmd.run: + - name: echo test diff --git a/tests/integration/ssh/test_state.py b/tests/integration/ssh/test_state.py index 45bdee27aa..031d91896a 100644 --- a/tests/integration/ssh/test_state.py +++ b/tests/integration/ssh/test_state.py @@ -24,9 +24,12 @@ class SSHStateTest(SSHCase): ''' testing the state system with salt-ssh ''' - def _check_dict_ret(self, ret, val, exp_ret): + def _check_dict_ret(self, ret, val, exp_ret, equal=True): for key, value in ret.items(): - self.assertEqual(value[val], exp_ret) + if equal: + self.assertEqual(value[val], exp_ret) + else: + self.assertNotEqual(value[val], exp_ret) def _check_request(self, empty=False): check = self.run_function('state.check_request', wipe=False) @@ -50,12 +53,32 @@ class SSHStateTest(SSHCase): ''' test state.sls_id with salt-ssh ''' + # check state.sls_id with test=True + ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS, + 'test=True']) + self._check_dict_ret(ret=ret, val='comment', + exp_ret='The file /tmp/test is set to be changed') + + # check state.sls_id without test=True ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS]) self._check_dict_ret(ret=ret, val='__sls__', exp_ret=SSH_SLS) + # make sure the other id in the state was not run + self._check_dict_ret(ret=ret, val='__id__', + exp_ret='second_id', equal=False) + + check_file = self.run_function('file.file_exists', ['/tmp/test']) self.assertTrue(check_file) + def test_state_sls_wrong_id(self): + ''' + test state.sls_id when id does not exist + ''' + # check state.sls_id with test=True + ret = self.run_function('state.sls_id', ['doesnotexist', SSH_SLS]) + assert 'No matches for ID' in ret + def test_state_show_sls(self): ''' test state.show_sls with salt-ssh From f69932f50636cc99ac748c65544aff884921dc5f Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Thu, 12 Jul 2018 12:37:24 -0400 Subject: [PATCH 3/3] fix pylint --- salt/client/ssh/wrapper/state.py | 3 ++- tests/integration/ssh/test_state.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/client/ssh/wrapper/state.py b/salt/client/ssh/wrapper/state.py index 6b8f39325e..6205c1acee 100644 --- a/salt/client/ssh/wrapper/state.py +++ b/salt/client/ssh/wrapper/state.py @@ -31,6 +31,7 @@ __func_alias__ = { } log = logging.getLogger(__name__) + def _ssh_state(chunks, __opts__, __context__, __pillar__, __salt__, st_kwargs, kwargs, test=False): ''' @@ -78,7 +79,7 @@ def _ssh_state(chunks, __opts__, __context__, __pillar__, __salt__, st_kwargs, try: return json.loads(stdout, object_hook=salt.utils.decode_dict) except Exception as e: - log.error("JSON Render failed for: {0}\n{1}".format(stdout, stderr)) + log.error("JSON Render failed for: %s\n%s", stdout, stderr) log.error(str(e)) # If for some reason the json load fails, return the stdout diff --git a/tests/integration/ssh/test_state.py b/tests/integration/ssh/test_state.py index 031d91896a..6b0c53d74b 100644 --- a/tests/integration/ssh/test_state.py +++ b/tests/integration/ssh/test_state.py @@ -67,7 +67,6 @@ class SSHStateTest(SSHCase): self._check_dict_ret(ret=ret, val='__id__', exp_ret='second_id', equal=False) - check_file = self.run_function('file.file_exists', ['/tmp/test']) self.assertTrue(check_file)