feat: display helm cmd in state output

Fixes salt-formulas/salt-formula-helm#5
diff --git a/_modules/helm.py b/_modules/helm.py
index 3e10683..c92f4d2 100644
--- a/_modules/helm.py
+++ b/_modules/helm.py
@@ -6,15 +6,10 @@
 
 LOG = logging.getLogger(__name__)
 
-def ok_or_output(cmd, prefix=None):
-    ret = __salt__['cmd.run_all'](**cmd)
-    if ret['retcode'] == 0:
-        return None
-    msg = "Stdout:\n{0[stdout]}\nStderr:\n{0[stderr]}".format(ret)
-    if prefix:
-        msg = prefix + ':\n' + msg
-    return msg
-
+class HelmExecutionError(CommandExecutionError):
+  def __init__(self, cmd, error):
+    self.cmd = cmd
+    self.error = error
 
 def _helm_cmd(*args, **kwargs):
     if kwargs.get('tiller_host'):
@@ -38,6 +33,24 @@
         'env': env,
     }
 
+def _cmd_and_result(*args, **kwargs):
+  cmd = _helm_cmd(*args, **kwargs)
+  env_string = "".join(['%s="%s" ' % (k, v) for (k, v) in cmd.get('env', {}).items()])
+  cmd_string = env_string + " ".join(cmd['cmd'])
+  result = None
+  try:
+    result = __salt__['cmd.run_all'](**cmd)
+    if result['retcode'] != 0:
+      raise CommandExecutionError(result['stderr'])
+    return {
+      'cmd': cmd_string,
+      'stdout': result['stdout'],
+      'stderr': result['stderr']
+    }
+  except CommandExecutionError as e:
+    raise HelmExecutionError(cmd_string, e)
+
+
 def _parse_release(output):
   result = {}
   chart_match = re.search(r'CHART\: ([^0-9]+)-([^\s]+)', output)
@@ -110,11 +123,7 @@
   url
       The url for the chart repository.
   '''
-  cmd = _helm_cmd('repo', 'add', name, url, **kwargs)
-  ret = __salt__['cmd.run_all'](**cmd)
-  if ret['retcode'] != 0:
-    raise CommandExecutionError(ret['stderr'])
-  return ret['stdout']
+  return _cmd_and_result('repo', 'add', name, url, **kwargs)
 
 def remove_repo(name, **kwargs):
   '''
@@ -124,11 +133,7 @@
   name
       The name (as registered with the Helm client) for the repository to remove
   '''
-  cmd = _helm_cmd('repo', 'remove', name, **kwargs)
-  ret = __salt__['cmd.run_all'](**cmd)
-  if ret['retcode'] != 0:
-    raise CommandExecutionError(ret['stderr'])
-  return ret['stdout']
+  return _cmd_and_result('repo', 'remove', name, **kwargs)
 
 def manage_repos(present={}, absent=[], exclusive=False, **kwargs):
   '''
@@ -206,7 +211,7 @@
       result['added'].append({ 
         'name': name, 
         'url': url, 
-        'stdout': add_repo(name, url, **kwargs)
+        'stdout': add_repo(name, url, **kwargs)['stdout']
       })
       existing_repos = {
         n: u for (n, u) in existing_repos.iteritems() if name != n
@@ -240,7 +245,7 @@
     try:
       result['removed'].append({ 
         'name': name, 
-        'stdout': remove_repo(name, **kwargs) 
+        'stdout': remove_repo(name, **kwargs) ['stdout']
       })
     except CommandExecutionError as e:
       result['failed'].append({ 
@@ -254,8 +259,7 @@
   Ensures the local helm repository cache for each repository is up to date. 
   Proxies the `helm repo update` command.
   '''
-  cmd = _helm_cmd('repo', 'update', **kwargs)
-  return __salt__['cmd.run_stdout'](**cmd)
+  return _cmd_and_result('repo', 'update', **kwargs)
 
 def get_release(name, tiller_namespace="kube-system", **kwargs):
   '''
@@ -311,19 +315,19 @@
         args += ['--version', version]
     if values_file is not None:
         args += ['--values', values_file]
-    cmd = _helm_cmd('install', '--namespace', namespace, '--name', name, chart_name, 
-                    *args, **kwargs)
-    LOG.debug('Creating release with args: %s', cmd)
-    return ok_or_output(cmd, 'Failed to create release "{}"'.format(name))
-
+    return _cmd_and_result(
+      'install', chart_name,
+      '--namespace', namespace, 
+      '--name', name,  
+      *args, **kwargs
+    )
 
 def release_delete(name, tiller_namespace='kube-system', **kwargs):
     '''
     Delete and purge any release found with the supplied name.
     '''
     kwargs['tiller_namespace'] = tiller_namespace
-    cmd = _helm_cmd('delete', '--purge', name, **kwargs)
-    return ok_or_output(cmd, 'Failed to delete release "{}"'.format(name))
+    return _cmd_and_result('delete', '--purge', name, **kwargs)
 
 
 def release_upgrade(name, chart_name, namespace='default',
@@ -343,9 +347,11 @@
       args += ['--version', version]
     if values_file is not None:
       args += ['--values', values_file]
-    cmd = _helm_cmd('upgrade', '--namespace', namespace, name, chart_name, **kwargs)
-    LOG.debug('Upgrading release with args: %s', cmd)
-    return ok_or_output(cmd, 'Failed to upgrade release "{}"'.format(name))
+    return _cmd_and_result(
+      'upgrade', name, chart_name,
+      '--namespace', namespace,  
+      **kwargs
+    )
 
 def install_chart_dependencies(chart_path, **kwargs):
   '''
@@ -355,8 +361,7 @@
   chart_path
       The path to the chart for which to install dependencies
   '''
-  cmd = _helm_cmd('dependency', 'build', **kwargs)
-  return __salt__['cmd.run_stdout'](cwd=chart_path, **cmd)
+  return _cmd_and_result('dependency', 'build', **kwargs)
 
 def package(path, destination = None, **kwargs):
   '''
@@ -373,5 +378,4 @@
   if destination:
     args += ["-d", destination]
   
-  cmd = _helm_cmd('package', path, *args, **kwargs)
-  return __salt__['cmd.run_stdout'](**cmd)
+  return _cmd_and_result('package', path, *args, **kwargs)
diff --git a/_states/helm_release.py b/_states/helm_release.py
index 7a66b10..dae657a 100644
--- a/_states/helm_release.py
+++ b/_states/helm_release.py
@@ -68,23 +68,28 @@
     kwargs['tiller_namespace'] = tiller_namespace
     old_release = __salt__['helm.get_release'](name, **kwargs)
     if not old_release:
-        err = __salt__['helm.release_create'](
+      try:
+        result = __salt__['helm.release_create'](
             name, chart_name, namespace, version, values_file, **kwargs
         )
-        if err:
-            return _failure(name, err)
         return {
+          'name': name,
+          'changes': {
             'name': name,
-            'changes': {
-                'name': name,
-                'chart_name': chart_name,
-                'namespace': namespace,
-                'version': version,
-                'values': _get_values_from_file(values_file)
-            },
-            'result': True,
-            'comment': 'Release "{}" was created'.format(name),
+            'chart_name': chart_name,
+            'namespace': namespace,
+            'version': version,
+            'values': _get_values_from_file(values_file),
+            'stdout': result.get('stdout')
+          },
+          'result': True,
+          'comment': ('Release "%s" was created' % name + 
+                      '\nExecuted command: %s' % result['cmd'])
         }
+      except CommandExecutionError as e:
+        msg = (("Failed to create new release: %s" % e.error) +
+               "\nExecuted command: %s" % e.cmd)
+        return _failure(name, msg)
 
     changes = {}
     warnings = []
@@ -120,29 +125,35 @@
     module_fn = 'helm.release_upgrade'
     if changes.get("namespace"):
       LOG.debug("purging old release (%s) due to namespace change" % name)
-      err = __salt__['helm.release_delete'](name, **kwargs)
-      if err:
-        return _failure(name, err, changes)
+      try:
+        result = __salt__['helm.release_delete'](name, **kwargs)
+      except CommandExecutionError as e:
+        msg = ("Failed to delete release for namespace change: %s" % e.error +
+               "\nExecuted command: %s" % e.cmd)
+        return _failure(name, msg, changes)
+
       module_fn = 'helm.release_create'
       warnings.append('Release (%s) was replaced due to namespace change' % name)
 
-    err = __salt__[module_fn](
+    try:
+      result = __salt__[module_fn](
         name, chart_name, namespace, version, values_file, **kwargs
-    )
-    if err:
-      return _failure(name, err, changes)
-
-    ret = {
+      )
+      changes.update({ 'stdout': result.get('stdout') })
+      ret = {
         'name': name,
         'changes': changes,
         'result': True,
-        'comment': 'Release "{}" was updated'.format(name),
-    }
+        'comment': 'Release "%s" was updated\nExecuted command: %s' % (name, result['cmd'])
+      }
+      if warnings:
+        ret['warnings'] = warnings
 
-    if warnings:
-      ret['warnings'] = warnings
-
-    return ret
+      return ret
+    except CommandExecutionError as e:
+      msg = ("Failed to delete release for namespace change: %s" % e.error +
+             "\nExecuted command: %s" % e.cmd)
+      return _failure(name, msg, changes)
 
 
 def absent(name, tiller_namespace='kube-system', **kwargs):
@@ -160,14 +171,16 @@
             'name': name,
             'changes': {},
             'result': True,
-            'comment': 'Release "{}" doesn\'t exist'.format(name),
+            'comment': 'Release "%s" doesn\'t exist' % name
         }
-    err = __salt__['helm.release_delete'](name, **kwargs)
-    if err:
-        return _failure(name, err)
-    return {
+    try:
+      result = __salt__['helm.release_delete'](name, **kwargs)
+      return {
         'name': name,
-        'changes': {name: 'DELETED'},
+        'changes': { name: 'DELETED', 'stdout': result['stdout'] },
         'result': True,
-        'comment': 'Release "{}" was deleted'.format(name),
-    }
+        'comment': 'Release "%s" was deleted\nExecuted command: %s' % (name, result['cmd'])
+      }
+    except CommandExecutionError as e:
+      return _failure(e.cmd, e.error)
+
diff --git a/_states/helm_repos.py b/_states/helm_repos.py
index ee1d556..5c41c0b 100644
--- a/_states/helm_repos.py
+++ b/_states/helm_repos.py
@@ -77,25 +77,29 @@
          'result': True,
          'comment': 'Successfully synced repositories: ' }
   
-  output = None
+
   try:
-    output = __salt__['helm.update_repos'](helm_home=helm_home)
+    result = __salt__['helm.update_repos'](helm_home=helm_home)
+    cmd_str = "\nExecuted command: %s" % result['cmd']
+
+    success_repos = re.findall(
+      r'Successfully got an update from the \"([^\"]+)\"', result['stdout'])
+    failed_repos = re.findall(
+      r'Unable to get an update from the \"([^\"]+)\"', result['stdout'])
+    
+    if failed_repos and len(failed_repos) > 0:
+      ret['result'] = False
+      ret['changes']['succeeded'] = success_repos
+      ret['changes']['failed'] = failed_repos
+      ret['comment'] = 'Failed to sync against some repositories' + cmd_str
+    else:
+      ret['comment'] += "%s" % success_repos + cmd_str
+    
   except CommandExecutionError as e:
+    ret['name'] = e.cmd
     ret['result'] = False
-    ret['comment'] = "Failed to update repos: %s" % e
+    ret['comment'] = ("Failed to update repos: %s" % e.error + 
+                      "\nExecuted command: %s" % e.cmd)
     return ret
 
-  success_repos = re.findall(
-    r'Successfully got an update from the \"([^\"]+)\"', output)
-  failed_repos = re.findall(
-    r'Unable to get an update from the \"([^\"]+)\"', output)
-  
-  if failed_repos and len(failed_repos) > 0:
-    ret['result'] = False
-    ret['changes']['succeeded'] = success_repos
-    ret['changes']['failed'] = failed_repos
-    ret['comment'] = 'Failed to sync against some repositories'
-  else:
-    ret['comment'] += "%s" % success_repos
-  
   return ret
\ No newline at end of file