Browse Source

AMBARI-8445. Resource management should support working under sudo (aonishuk)

Andrew Onishuk 10 năm trước cách đây
mục cha
commit
68c5572817
21 tập tin đã thay đổi với 254 bổ sung221 xóa
  1. 12 13
      ambari-agent/src/test/python/resource_management/TestDirectoryResource.py
  2. 4 6
      ambari-agent/src/test/python/resource_management/TestExecuteResource.py
  3. 28 39
      ambari-agent/src/test/python/resource_management/TestFileResource.py
  4. 3 3
      ambari-agent/src/test/python/resource_management/TestGroupResource.py
  5. 6 5
      ambari-agent/src/test/python/resource_management/TestLinkResource.py
  6. 9 9
      ambari-agent/src/test/python/resource_management/TestPackageResource.py
  7. 30 39
      ambari-agent/src/test/python/resource_management/TestPropertiesFileResource.py
  8. 10 10
      ambari-agent/src/test/python/resource_management/TestUserResource.py
  9. 13 29
      ambari-agent/src/test/python/resource_management/TestXmlConfigResource.py
  10. 2 2
      ambari-common/src/main/python/resource_management/core/providers/accounts.py
  11. 4 4
      ambari-common/src/main/python/resource_management/core/providers/package/apt.py
  12. 2 2
      ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py
  13. 2 2
      ambari-common/src/main/python/resource_management/core/providers/package/zypper.py
  14. 37 25
      ambari-common/src/main/python/resource_management/core/providers/system.py
  15. 2 1
      ambari-common/src/main/python/resource_management/core/resources/system.py
  16. 18 19
      ambari-common/src/main/python/resource_management/core/shell.py
  17. 62 0
      ambari-common/src/main/python/resource_management/core/sudo.py
  18. 2 2
      ambari-common/src/main/python/resource_management/libraries/providers/hdfs_directory.py
  19. 0 3
      ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java
  20. 2 2
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HIVE/package/scripts/hive_service.py
  21. 6 6
      ambari-server/src/test/python/stacks/2.0.6/HIVE/test_hive_server.py

+ 12 - 13
ambari-agent/src/test/python/resource_management/TestDirectoryResource.py

@@ -16,24 +16,23 @@ See the License for the specific language governing permissions and
 limitations under the License.
 '''
 
-from stacks.utils.RMFTestCase import *
 from unittest import TestCase
 from mock.mock import patch, MagicMock
 import os
 import shutil
 from resource_management.core.system import System
-from resource_management.core import Environment, Fail
+from resource_management.core import Environment, Fail, sudo
 from resource_management.core.resources import Directory
 
 @patch.object(System, "os_family", new = 'redhat')
-class TestFileResource(TestCase):
+class TestDirectoryResource(TestCase):
   
   @patch.object(os.path, "exists")
-  @patch.object(os, "makedirs")
+  @patch.object(sudo, "makedirs")
   @patch.object(os.path, "isdir")
   @patch.object(os, "stat")
-  @patch.object(os,"chmod")
-  @patch.object(os,"chown")
+  @patch.object(sudo,"chmod")
+  @patch.object(sudo,"chown")
   @patch("resource_management.core.providers.system._coerce_uid")
   @patch("resource_management.core.providers.system._coerce_gid")
   def test_create_directory_recursive(self, _coerce_gid_mock, _coerce_uid_mock,
@@ -57,16 +56,16 @@ class TestFileResource(TestCase):
       
     os_makedirs_mock.assert_called_with('/a/b/c/d', 0777)
     os_chmod_mock.assert_called_with('/a/b/c/d', 0777)
-    os_chown_mock.assert_any_call('/a/b/c/d', 66, -1)
-    os_chown_mock.assert_any_call('/a/b/c/d', -1, 77)
+    os_chown_mock.assert_any_call('/a/b/c/d', 'hdfs', None)
+    os_chown_mock.assert_any_call('/a/b/c/d', None, 'hadoop')
   
   @patch.object(os.path, "exists")
   @patch.object(os.path, "dirname")
   @patch.object(os.path, "isdir")
-  @patch.object(os, "mkdir")
+  @patch.object(sudo, "makedir")
   @patch.object(os, "stat")
-  @patch.object(os,"chmod")
-  @patch.object(os,"chown")
+  @patch.object(sudo,"chmod")
+  @patch.object(sudo,"chown")
   @patch("resource_management.core.providers.system._coerce_uid")
   @patch("resource_management.core.providers.system._coerce_gid")
   def test_create_directory_not_recursive(self, _coerce_gid_mock, _coerce_uid_mock,
@@ -90,8 +89,8 @@ class TestFileResource(TestCase):
       
     mkdir_mock.assert_called_with('/a/b/c/d', 0777)
     os_chmod_mock.assert_called_with('/a/b/c/d', 0777)
-    os_chown_mock.assert_any_call('/a/b/c/d', 66, -1)
-    os_chown_mock.assert_any_call('/a/b/c/d', -1, 77)
+    os_chown_mock.assert_any_call('/a/b/c/d', 'hdfs', None)
+    os_chown_mock.assert_any_call('/a/b/c/d', None, 'hadoop')
     
   @patch.object(os.path, "exists")
   @patch.object(os.path, "dirname")

+ 4 - 6
ambari-agent/src/test/python/resource_management/TestExecuteResource.py

@@ -16,7 +16,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 '''
 
-from stacks.utils.RMFTestCase import *
 from unittest import TestCase
 from mock.mock import patch, MagicMock, call
 
@@ -91,8 +90,8 @@ class TestExecuteResource(TestCase):
       execute_resource = Execute('echo "1"',
                                  path=["/test/one", "test/two"]
       )
-    expected_command = 'export PATH=$PATH:/test/one:test/two ; echo "1"'
-    self.assertEqual(popen_mock.call_args_list[0][0][0][3], expected_command)
+    expected_command = ['/bin/bash', '--login', '-c', 'echo "1"']
+    self.assertEqual(popen_mock.call_args_list[0][0][0], expected_command)
 
   @patch('time.sleep')
   @patch.object(subprocess, "Popen")
@@ -177,9 +176,8 @@ class TestExecuteResource(TestCase):
                                  environment={'JAVA_HOME': '/test/java/home',
                                               'PATH': "/bin"}
       )
-    expected_command = 'export  PATH=$PATH:/bin JAVA_HOME=/test/java/home ; echo "1"'
-    self.assertEqual(popen_mock.call_args_list[0][0][0][4], expected_user)
-    self.assertEqual(popen_mock.call_args_list[0][0][0][6], expected_command)
+    expected_command = '/usr/bin/sudo -Hsu test_user <<< \'export PATH=' + os.environ['PATH'] + ':/bin JAVA_HOME=/test/java/home; echo "1"\'' 
+    self.assertEqual(popen_mock.call_args_list[0][0][0][3], expected_command)
 
 
   @patch.object(subprocess, "Popen")

+ 28 - 39
ambari-agent/src/test/python/resource_management/TestFileResource.py

@@ -24,6 +24,7 @@ import sys
 from resource_management.core import Environment, Fail
 from resource_management.core.resources import File
 from resource_management.core.system import System
+from resource_management.core import sudo
 import resource_management.core.providers.system
 import resource_management
 
@@ -76,17 +77,16 @@ class TestFileResource(TestCase):
     self.assertTrue(dirname_mock.called)
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "read_file")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
-  def test_action_create_non_existent_file(self, isdir_mock, exists_mock, open_mock, ensure_mock):
+  def test_action_create_non_existent_file(self, isdir_mock, exists_mock, create_file_mock, read_file_mock, ensure_mock):
     """
     Tests if 'create' action create new non existent file and write proper data
     """
     isdir_mock.side_effect = [False, True]
     exists_mock.return_value = False
-    new_file = MagicMock()
-    open_mock.return_value = new_file
     with Environment('/') as env:
       File('/directory/file',
            action='create',
@@ -95,24 +95,21 @@ class TestFileResource(TestCase):
       )
     
 
-    open_mock.assert_called_with('/directory/file', 'wb')
-    new_file.__enter__().write.assert_called_with('file-content')
-    self.assertEqual(open_mock.call_count, 1)
+    create_file_mock.assert_called_with('/directory/file', 'file-content')
+    self.assertEqual(create_file_mock.call_count, 1)
     ensure_mock.assert_called()
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "read_file")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
-  def test_action_create_replace(self, isdir_mock, exists_mock, open_mock, ensure_mock):
+  def test_action_create_replace(self, isdir_mock, exists_mock, create_file_mock, read_file_mock, ensure_mock):
     """
     Tests if 'create' action rewrite existent file with new data
     """
     isdir_mock.side_effect = [False, True]
-    old_file, new_file = MagicMock(), MagicMock()
-    open_mock.side_effect = [old_file, new_file]
-    old_file.read.return_value = 'old-content'
     exists_mock.return_value = True
 
     with Environment('/') as env:
@@ -123,16 +120,11 @@ class TestFileResource(TestCase):
            content='new-content'
       )
 
-    
-    old_file.read.assert_called()
-    new_file.__enter__().write.assert_called_with('new-content')
-    ensure_mock.assert_called()
-    self.assertEqual(open_mock.call_count, 2)
-    open_mock.assert_any_call('/directory/file', 'rb')
-    open_mock.assert_any_call('/directory/file', 'wb')
+    read_file_mock.assert_called_with('/directory/file')    
+    create_file_mock.assert_called_with('/directory/file', 'new-content')
 
 
-  @patch.object(os, "unlink")
+  @patch.object(sudo, "unlink")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   def test_action_delete_is_directory(self, isdir_mock, exist_mock, unlink_mock):
@@ -158,7 +150,7 @@ class TestFileResource(TestCase):
     self.assertEqual(exist_mock.call_count, 0)
     self.assertEqual(unlink_mock.call_count, 0)
 
-  @patch.object(os, "unlink")
+  @patch.object(sudo, "unlink")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   def test_action_delete(self, isdir_mock, exist_mock, unlink_mock):
@@ -204,15 +196,15 @@ class TestFileResource(TestCase):
 
   @patch.object(resource_management.core.Environment, "backup_file")
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "read_file")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
-  def test_attribute_backup(self, isdir_mock, exists_mock, open_mock, ensure_mock, backup_file_mock):
+  def test_attribute_backup(self, isdir_mock, exists_mock, create_file_mock,  read_file_mock, ensure_mock, backup_file_mock):
     """
     Tests 'backup' attribute
     """
     isdir_mock.side_effect = [False, True, False, True]
-    open_mock.return_value = MagicMock()
     exists_mock.return_value = True
 
     with Environment('/') as env:
@@ -271,13 +263,13 @@ class TestFileResource(TestCase):
 
   @patch("resource_management.core.providers.system._coerce_uid")
   @patch("resource_management.core.providers.system._coerce_gid")
-  @patch.object(os, "chown")
-  @patch.object(os, "chmod")
+  @patch.object(sudo, "chown")
+  @patch.object(sudo, "chmod")
   @patch.object(os, "stat")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
-  def test_ensure_metadata(self, isdir_mock, exists_mock, open_mock, stat_mock, chmod_mock, chown_mock, gid_mock,
+  def test_ensure_metadata(self, isdir_mock, exists_mock, create_file_mock, stat_mock, chmod_mock, chown_mock, gid_mock,
                            uid_mock):
     """
     Tests if _ensure_metadata changes owner, usergroup and permissions of file to proper values
@@ -305,8 +297,8 @@ class TestFileResource(TestCase):
       )
     
 
-    open_mock.assert_called_with('/directory/file', 'wb')
-    self.assertEqual(open_mock.call_count, 1)
+    create_file_mock.assert_called_with('/directory/file', 'file-content')
+    self.assertEqual(create_file_mock.call_count, 1)
     stat_mock.assert_called_with('/directory/file')
     self.assertEqual(chmod_mock.call_count, 1)
     self.assertEqual(chown_mock.call_count, 2)
@@ -333,21 +325,18 @@ class TestFileResource(TestCase):
 
   @patch("resource_management.core.providers.system._ensure_metadata")
   @patch("resource_management.core.providers.system.FileProvider._get_content")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "read_file")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
-  def test_action_create_encoding(self, isdir_mock, exists_mock, open_mock, get_content_mock ,ensure_mock):
+  def test_action_create_encoding(self, isdir_mock, exists_mock, create_file_mock, read_file_mock, get_content_mock ,ensure_mock):
 
     isdir_mock.side_effect = [False, True]
-    exists_mock.return_value = True
     content_mock = MagicMock()
     old_content_mock = MagicMock()
     get_content_mock.return_value = content_mock
-    new_file = MagicMock()
-    open_mock.return_value = new_file
-    enter_file_mock = MagicMock()
-    enter_file_mock.read = MagicMock(return_value=old_content_mock)
-    new_file.__enter__ = MagicMock(return_value=enter_file_mock)
+    read_file_mock.return_value = old_content_mock
+    exists_mock.return_value = True
     with Environment('/') as env:
       File('/directory/file',
            action='create',
@@ -357,7 +346,7 @@ class TestFileResource(TestCase):
       )
 
 
-    open_mock.assert_called_with('/directory/file', 'wb')
+    read_file_mock.assert_called_with('/directory/file')
     content_mock.encode.assert_called_with('UTF-8')
     old_content_mock.decode.assert_called_with('UTF-8')
 

+ 3 - 3
ambari-agent/src/test/python/resource_management/TestGroupResource.py

@@ -45,7 +45,7 @@ class TestGroupResource(TestCase):
     
 
     self.assertEqual(popen_mock.call_count, 1)
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'groupadd -p secure hadoop'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'groupadd -p secure hadoop'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     getgrnam_mock.assert_called_with('hadoop')
 
 
@@ -66,7 +66,7 @@ class TestGroupResource(TestCase):
     
 
     self.assertEqual(popen_mock.call_count, 1)
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'groupmod -p secure -g 2 mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'groupmod -p secure -g 2 mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     getgrnam_mock.assert_called_with('mapred')
 
 
@@ -90,7 +90,7 @@ class TestGroupResource(TestCase):
     except Fail:
       pass
     self.assertEqual(popen_mock.call_count, 1)
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'groupmod -p secure -g 2 mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'groupmod -p secure -g 2 mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     getgrnam_mock.assert_called_with('mapred')
 
 

+ 6 - 5
ambari-agent/src/test/python/resource_management/TestLinkResource.py

@@ -20,6 +20,7 @@ from unittest import TestCase
 from mock.mock import patch, MagicMock
 
 from resource_management.core import Environment, Fail
+from resource_management.core import sudo
 from resource_management.core.system import System
 from resource_management.core.resources.system import Link
 
@@ -31,8 +32,8 @@ class TestLinkResource(TestCase):
   @patch.object(os.path, "realpath")
   @patch.object(os.path, "lexists")
   @patch.object(os.path, "islink")
-  @patch.object(os, "unlink")
-  @patch.object(os, "symlink")
+  @patch.object(sudo, "unlink")
+  @patch.object(sudo, "symlink")
   def test_action_create_relink(self, symlink_mock, unlink_mock, 
                          islink_mock, lexists_mock,
                          realmock):
@@ -67,7 +68,7 @@ class TestLinkResource(TestCase):
                        str(e))
         
   @patch.object(os.path, "lexists")
-  @patch.object(os, "symlink")
+  @patch.object(sudo, "symlink")
   def test_action_create_symlink_clean_create(self, symlink_mock, lexists_mock):
     lexists_mock.return_value = False
     
@@ -81,7 +82,7 @@ class TestLinkResource(TestCase):
   @patch.object(os.path, "isdir")
   @patch.object(os.path, "exists")  
   @patch.object(os.path, "lexists")
-  @patch.object(os, "link")
+  @patch.object(sudo, "link")
   def test_action_create_hardlink_clean_create(self, link_mock, lexists_mock,
                                         exists_mock, isdir_mock):
     lexists_mock.return_value = False
@@ -134,7 +135,7 @@ class TestLinkResource(TestCase):
         self.assertEqual("Failed to apply Link['/some_path'], cannot create hard link to a directory (/a/b/link_to_path)",
                        str(e)) 
         
-  @patch.object(os, "unlink")
+  @patch.object(sudo, "unlink")
   @patch.object(os.path, "exists")
   def test_action_delete(self, exists_mock, unlink_mock):     
     exists_mock.return_value = True

+ 9 - 9
ambari-agent/src/test/python/resource_management/TestPackageResource.py

@@ -37,11 +37,11 @@ class TestPackageResource(TestCase):
       )
     call_mock.assert_has_calls([call("dpkg --get-selections | grep ^some-package$ | grep -v deinstall"),
                                 call("DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get -q -o Dpkg::Options::='--force-confdef'"
-                                      " --allow-unauthenticated --assume-yes install some-package"),
-                                call("apt-get update -qq")
+                                      " --allow-unauthenticated --assume-yes install some-package", sudo=True),
+                                call("apt-get update -qq", sudo=True)
                               ])
     
-    shell_mock.assert_has_calls([call("DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get -q -o Dpkg::Options::='--force-confdef' --allow-unauthenticated --assume-yes install some-package")
+    shell_mock.assert_has_calls([call("DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get -q -o Dpkg::Options::='--force-confdef' --allow-unauthenticated --assume-yes install some-package", sudo=True)
                               ])
   
   @patch.object(shell, "call")
@@ -54,7 +54,7 @@ class TestPackageResource(TestCase):
       )
     call_mock.assert_has_calls([call("dpkg --get-selections | grep ^some-package$ | grep -v deinstall"),
                                 call("DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get -q -o Dpkg::Options::='--force-confdef'"
-                                      " --allow-unauthenticated --assume-yes install some-package")
+                                      " --allow-unauthenticated --assume-yes install some-package", sudo=True)
                               ])
     
     self.assertEqual(shell_mock.call_count, 0, "shell.checked_call shouldn't be called")
@@ -70,7 +70,7 @@ class TestPackageResource(TestCase):
       Package("some_package",
       )
     call_mock.assert_called_with('installed_pkgs=`rpm -qa some_package` ; [ ! -z "$installed_pkgs" ]')
-    shell_mock.assert_called_with("/usr/bin/yum -d 0 -e 0 -y install some_package")
+    shell_mock.assert_called_with("/usr/bin/yum -d 0 -e 0 -y install some_package", sudo=True)
 
   @patch.object(shell, "call")
   @patch.object(shell, "checked_call")
@@ -81,7 +81,7 @@ class TestPackageResource(TestCase):
       Package("some_package",
       )
     call_mock.assert_called_with('rpm -qa | grep ^some_package')
-    shell_mock.assert_called_with("/usr/bin/zypper --quiet install --auto-agree-with-licenses --no-confirm some_package")
+    shell_mock.assert_called_with("/usr/bin/zypper --quiet install --auto-agree-with-licenses --no-confirm some_package", sudo=True)
 
   @patch.object(shell, "call", new = MagicMock(return_value=(0, None)))
   @patch.object(shell, "checked_call")
@@ -109,7 +109,7 @@ class TestPackageResource(TestCase):
       Package("some_package",
               action = "remove"
       )
-    shell_mock.assert_called_with("/usr/bin/yum -d 0 -e 0 -y erase some_package")
+    shell_mock.assert_called_with("/usr/bin/yum -d 0 -e 0 -y erase some_package", sudo=True)
 
   @patch.object(shell, "call", new = MagicMock(return_value=(0, None)))
   @patch.object(shell, "checked_call")
@@ -119,7 +119,7 @@ class TestPackageResource(TestCase):
       Package("some_package",
               action = "remove"
       )
-    shell_mock.assert_called_with("/usr/bin/zypper --quiet remove --no-confirm some_package")
+    shell_mock.assert_called_with("/usr/bin/zypper --quiet remove --no-confirm some_package", sudo=True)
 
   @patch.object(shell, "call", new = MagicMock(return_value=(1, None)))
   @patch.object(shell, "checked_call")
@@ -129,7 +129,7 @@ class TestPackageResource(TestCase):
       Package("some_package",
               version = "3.5.0"
       )
-    shell_mock.assert_called_with("/usr/bin/yum -d 0 -e 0 -y install some_package-3.5.0")
+    shell_mock.assert_called_with("/usr/bin/yum -d 0 -e 0 -y install some_package-3.5.0", sudo=True)
 
   @replace_underscores
   def func_to_test(self, name):

+ 30 - 39
ambari-agent/src/test/python/resource_management/TestPropertiesFileResource.py

@@ -24,7 +24,7 @@ import os
 import time
 from unittest import TestCase
 from mock.mock import patch, MagicMock
-from resource_management.core import Environment
+from resource_management.core import Environment, sudo
 from resource_management.core.system import System
 from resource_management.libraries import PropertiesFile
 
@@ -37,7 +37,7 @@ class TestPropertiesFIleResource(TestCase):
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -45,7 +45,7 @@ class TestPropertiesFIleResource(TestCase):
                                                       time_asctime_mock,
                                                       os_path_isdir_mock,
                                                       os_path_exists_mock,
-                                                      open_mock,
+                                                      create_file_mock,
                                                       ensure_mock):
     """
     Tests if 'action_create' - creates new non existent file and write proper data
@@ -56,8 +56,8 @@ class TestPropertiesFIleResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 'Today is Wednesday'
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
+    
+    
 
     with Environment('/') as env:
       PropertiesFile('/somewhere_in_system/one_file.properties',
@@ -65,14 +65,12 @@ class TestPropertiesFIleResource(TestCase):
                      properties={}
       )
 
-    open_mock.assert_called_with('/somewhere_in_system/one_file.properties', 'wb')
-    result_file.__enter__().write.assert_called_with( u'# Generated by Apache Ambari. Today is Wednesday\n    \n    \n')
-    self.assertEqual(open_mock.call_count, 1)
+    create_file_mock.assert_called_with('/somewhere_in_system/one_file.properties', u'# Generated by Apache Ambari. Today is Wednesday\n    \n    \n')
     ensure_mock.assert_called()
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -80,7 +78,7 @@ class TestPropertiesFIleResource(TestCase):
                                                    time_asctime_mock,
                                                    os_path_isdir_mock,
                                                    os_path_exists_mock,
-                                                   open_mock,
+                                                   create_file_mock,
                                                    ensure_mock):
     """
     Tests if 'action_create' - creates new non existent file and write proper data
@@ -91,8 +89,8 @@ class TestPropertiesFIleResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 'Some other day'
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
+    
+    
 
     with Environment('/') as env:
       PropertiesFile('file.txt',
@@ -100,14 +98,12 @@ class TestPropertiesFIleResource(TestCase):
                      properties={},
       )
 
-    open_mock.assert_called_with('/dir/and/dir/file.txt', 'wb')
-    result_file.__enter__().write.assert_called_with(u'# Generated by Apache Ambari. Some other day\n    \n    \n')
-    self.assertEqual(open_mock.call_count, 1)
+    create_file_mock.assert_called_with('/dir/and/dir/file.txt', u'# Generated by Apache Ambari. Some other day\n    \n    \n')
     ensure_mock.assert_called()
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -115,7 +111,7 @@ class TestPropertiesFIleResource(TestCase):
                                            time_asctime_mock,
                                            os_path_isdir_mock,
                                            os_path_exists_mock,
-                                           open_mock,
+                                           create_file_mock,
                                            ensure_mock):
     """
     Tests if 'action_create' - creates new non existent file and write proper data
@@ -127,23 +123,20 @@ class TestPropertiesFIleResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 777
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
+    
+    
 
     with Environment('/') as env:
       PropertiesFile('/dir/new_file',
                      properties={'property1': 'value1'},
       )
 
-    open_mock.assert_called_with('/dir/new_file',
-                                 'wb')
-    result_file.__enter__().write.assert_called_with(u'# Generated by Apache Ambari. 777\n    \nproperty1=value1\n    \n')
-    self.assertEqual(open_mock.call_count, 1)
+    create_file_mock.assert_called_with('/dir/new_file', u'# Generated by Apache Ambari. 777\n    \nproperty1=value1\n    \n')
     ensure_mock.assert_called()
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -151,7 +144,7 @@ class TestPropertiesFIleResource(TestCase):
                                                         time_asctime_mock,
                                                         os_path_isdir_mock,
                                                         os_path_exists_mock,
-                                                        open_mock,
+                                                        create_file_mock,
                                                         ensure_mock):
     """
     Tests if 'action_create' - creates new non existent file and write proper data
@@ -162,8 +155,8 @@ class TestPropertiesFIleResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 777
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
+    
+    
 
     with Environment('/') as env:
       PropertiesFile('/dir/new_file',
@@ -176,14 +169,13 @@ class TestPropertiesFIleResource(TestCase):
                      },
       )
 
-    open_mock.assert_called_with('/dir/new_file','wb')
-    result_file.__enter__().write.assert_called_with(u"# Generated by Apache Ambari. 777\n    \n=\nprop.1='.'yyyy-MM-dd-HH\nprop.2=INFO, openjpa\nprop.3=%d{ISO8601} %5p %c{1}:%L - %m%n\nprop.4=${oozie.log.dir}/oozie.log\nprop.empty=\n    \n")
-    self.assertEqual(open_mock.call_count, 1)
+    create_file_mock.assert_called_with('/dir/new_file', u"# Generated by Apache Ambari. 777\n    \n=\nprop.1='.'yyyy-MM-dd-HH\nprop.2=INFO, openjpa\nprop.3=%d{ISO8601} %5p %c{1}:%L - %m%n\nprop.4=${oozie.log.dir}/oozie.log\nprop.empty=\n    \n")
     ensure_mock.assert_called()
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "read_file")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -191,7 +183,8 @@ class TestPropertiesFIleResource(TestCase):
                                                     time_asctime_mock,
                                                     os_path_isdir_mock,
                                                     os_path_exists_mock,
-                                                    open_mock,
+                                                    create_file_mock,
+                                                    read_file_mock,
                                                     ensure_mock):
     """
     Tests if 'action_create' - rewrite file that exist
@@ -202,9 +195,9 @@ class TestPropertiesFIleResource(TestCase):
     os_path_exists_mock.return_value = True
     time_asctime_mock.return_value = 777
 
-    result_file = MagicMock()
-    result_file.read.return_value = 'old-content'
-    open_mock.return_value = result_file
+    
+    read_file_mock.return_value = 'old-content'
+    
 
     with Environment('/') as env:
       PropertiesFile('new_file',
@@ -212,8 +205,6 @@ class TestPropertiesFIleResource(TestCase):
                      properties={'property_1': 'value1'},
       )
 
-    result_file.read.assert_called()
-    open_mock.assert_called_with('/dir1/new_file', 'wb')
-    result_file.__enter__().write.assert_called_with(u'# Generated by Apache Ambari. 777\n    \nproperty_1=value1\n    \n')
-    self.assertEqual(open_mock.call_count, 2)
+    read_file_mock.assert_called()
+    create_file_mock.assert_called_with('/dir1/new_file', u'# Generated by Apache Ambari. 777\n    \nproperty_1=value1\n    \n')
     ensure_mock.assert_called()

+ 10 - 10
ambari-agent/src/test/python/resource_management/TestUserResource.py

@@ -38,7 +38,7 @@ class TestUserResource(TestCase):
     with Environment('/') as env:
       user = User("mapred", action = "create", shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'useradd -m -s /bin/bash mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'useradd -m -s /bin/bash mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -52,7 +52,7 @@ class TestUserResource(TestCase):
     with Environment('/') as env:
       user = User("mapred", action = "create", shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -s /bin/bash mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -s /bin/bash mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -81,7 +81,7 @@ class TestUserResource(TestCase):
       user = User("mapred", action = "create", comment = "testComment", 
           shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -c testComment -s /bin/bash mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -c testComment -s /bin/bash mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -96,7 +96,7 @@ class TestUserResource(TestCase):
       user = User("mapred", action = "create", home = "/test/home", 
           shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -s /bin/bash -d /test/home mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -s /bin/bash -d /test/home mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -111,7 +111,7 @@ class TestUserResource(TestCase):
       user = User("mapred", action = "create", password = "secure", 
           shell = "/bin/bash")    
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -s /bin/bash -p secure mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -s /bin/bash -p secure mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -125,7 +125,7 @@ class TestUserResource(TestCase):
     with Environment('/') as env:
       user = User("mapred", action = "create", shell = "/bin/sh")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -s /bin/sh mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -s /bin/sh mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -139,7 +139,7 @@ class TestUserResource(TestCase):
     with Environment('/') as env:
       user = User("mapred", action = "create", uid = "1", shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -s /bin/bash -u 1 mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -s /bin/bash -u 1 mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -153,7 +153,7 @@ class TestUserResource(TestCase):
     with Environment('/') as env:
       user = User("mapred", action = "create", gid = "1", shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -s /bin/bash -g 1 mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -s /bin/bash -g 1 mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -168,7 +168,7 @@ class TestUserResource(TestCase):
       user = User("mapred", action = "create", groups = ['1','2','3'], 
           shell = "/bin/bash")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'usermod -G 1,2,3 -s /bin/bash mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'usermod -G 1,2,3 -s /bin/bash mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)
 
   @patch.object(subprocess, "Popen")
@@ -181,5 +181,5 @@ class TestUserResource(TestCase):
     with Environment('/') as env:
       user = User("mapred", action = "create")
 
-    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', 'useradd -m mapred'], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
+    popen_mock.assert_called_with(['/bin/bash', '--login', '-c', "/usr/bin/sudo -s <<< 'useradd -m mapred'"], shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None)
     self.assertEqual(popen_mock.call_count, 1)

+ 13 - 29
ambari-agent/src/test/python/resource_management/TestXmlConfigResource.py

@@ -24,7 +24,7 @@ import os
 import time
 from unittest import TestCase
 from mock.mock import patch, MagicMock
-from resource_management.core import Environment
+from resource_management.core import Environment, sudo
 from resource_management.core.system import System
 from resource_management.libraries import XmlConfig
 
@@ -37,7 +37,7 @@ class TestXmlConfigResource(TestCase):
   """
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -45,7 +45,7 @@ class TestXmlConfigResource(TestCase):
                                           time_asctime_mock,
                                           os_path_isdir_mock,
                                           os_path_exists_mock,
-                                          open_mock,
+                                          create_file_mock,
                                           ensure_mock):
     """
     Tests if 'create' action - creates new non existent xml file and write proper data
@@ -55,9 +55,6 @@ class TestXmlConfigResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 'Wed 2014-02'
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
-
     with Environment('/') as env:
       XmlConfig('file.xml',
                 conf_dir='/dir/conf',
@@ -65,12 +62,11 @@ class TestXmlConfigResource(TestCase):
                 configuration_attributes={}
                 )
 
-    open_mock.assert_called_with('/dir/conf/file.xml', 'wb')
-    result_file.__enter__().write.assert_called_with(u'<!--Wed 2014-02-->\n    <configuration>\n    \n  </configuration>\n')
+    create_file_mock.assert_called_with('/dir/conf/file.xml', u'<!--Wed 2014-02-->\n    <configuration>\n    \n  </configuration>\n')
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -78,7 +74,7 @@ class TestXmlConfigResource(TestCase):
                                            time_asctime_mock,
                                            os_path_isdir_mock,
                                            os_path_exists_mock,
-                                           open_mock,
+                                           create_file_mock,
                                            ensure_mock):
     """
     Tests if 'create' action - creates new non existent xml file and write proper data
@@ -88,9 +84,6 @@ class TestXmlConfigResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 'Wed 2014-02'
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
-
     with Environment('/') as env:
       XmlConfig('file.xml',
                 conf_dir='/dir/conf',
@@ -98,12 +91,11 @@ class TestXmlConfigResource(TestCase):
                 configuration_attributes={'attr': {'property1': 'attr_value'}}
                 )
 
-    open_mock.assert_called_with('/dir/conf/file.xml', 'wb')
-    result_file.__enter__().write.assert_called_with(u'<!--Wed 2014-02-->\n    <configuration>\n    \n    <property>\n      <name>property1</name>\n      <value>value1</value>\n      <attr>attr_value</attr>\n    </property>\n    \n  </configuration>\n')
+    create_file_mock.assert_called_with('/dir/conf/file.xml', u'<!--Wed 2014-02-->\n    <configuration>\n    \n    <property>\n      <name>property1</name>\n      <value>value1</value>\n      <attr>attr_value</attr>\n    </property>\n    \n  </configuration>\n')
 
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -111,7 +103,7 @@ class TestXmlConfigResource(TestCase):
                                                         time_asctime_mock,
                                                         os_path_isdir_mock,
                                                         os_path_exists_mock,
-                                                        open_mock,
+                                                        create_file_mock,
                                                         ensure_mock):
     """
     Tests if 'create' action - creates new non existent xml file and write proper data
@@ -121,9 +113,6 @@ class TestXmlConfigResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 'Wed 2014-02'
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
-
     with Environment('/') as env:
       XmlConfig('file.xml',
                 conf_dir='/dir/conf',
@@ -155,11 +144,10 @@ class TestXmlConfigResource(TestCase):
                     }
                 })
 
-    open_mock.assert_called_with('/dir/conf/file.xml', 'wb')
-    result_file.__enter__().write.assert_called_with(u'<!--Wed 2014-02-->\n    <configuration>\n    \n    <property>\n      <name></name>\n      <value></value>\n    </property>\n    \n    <property>\n      <name>prop.1</name>\n      <value>&#39;.&#39;yyyy-MM-dd-HH</value>\n      <attr1>x</attr1>\n    </property>\n    \n    <property>\n      <name>prop.2</name>\n      <value>INFO, openjpa</value>\n    </property>\n    \n    <property>\n      <name>prop.3</name>\n      <value>%d{ISO8601} %5p %c{1}:%L - %m%n</value>\n      <attr2>value3</attr2>\n    </property>\n    \n    <property>\n      <name>prop.4</name>\n      <value>${oozie.log.dir}/oozie.log</value>\n      <attr_value_empty></attr_value_empty>\n      <attr2>value4</attr2>\n    </property>\n    \n    <property>\n      <name>prop.empty</name>\n      <value></value>\n      <attr_value_empty></attr_value_empty>\n    </property>\n    \n  </configuration>\n')
+    create_file_mock.assert_called_with('/dir/conf/file.xml', u'<!--Wed 2014-02-->\n    <configuration>\n    \n    <property>\n      <name></name>\n      <value></value>\n    </property>\n    \n    <property>\n      <name>prop.1</name>\n      <value>&#39;.&#39;yyyy-MM-dd-HH</value>\n      <attr1>x</attr1>\n    </property>\n    \n    <property>\n      <name>prop.2</name>\n      <value>INFO, openjpa</value>\n    </property>\n    \n    <property>\n      <name>prop.3</name>\n      <value>%d{ISO8601} %5p %c{1}:%L - %m%n</value>\n      <attr2>value3</attr2>\n    </property>\n    \n    <property>\n      <name>prop.4</name>\n      <value>${oozie.log.dir}/oozie.log</value>\n      <attr_value_empty></attr_value_empty>\n      <attr2>value4</attr2>\n    </property>\n    \n    <property>\n      <name>prop.empty</name>\n      <value></value>\n      <attr_value_empty></attr_value_empty>\n    </property>\n    \n  </configuration>\n')
 
   @patch("resource_management.core.providers.system._ensure_metadata")
-  @patch("__builtin__.open")
+  @patch.object(sudo, "create_file")
   @patch.object(os.path, "exists")
   @patch.object(os.path, "isdir")
   @patch.object(time, "asctime")
@@ -167,7 +155,7 @@ class TestXmlConfigResource(TestCase):
                                                   time_asctime_mock,
                                                   os_path_isdir_mock,
                                                   os_path_exists_mock,
-                                                  open_mock,
+                                                  create_file_mock,
                                                   ensure_mock):
     """
     Tests if 'create' action - creates new non existent xml file and writes proper data
@@ -177,9 +165,6 @@ class TestXmlConfigResource(TestCase):
     os_path_exists_mock.return_value = False
     time_asctime_mock.return_value = 'Wed 2014-02'
 
-    result_file = MagicMock()
-    open_mock.return_value = result_file
-
     with Environment('/') as env:
       XmlConfig('file.xml',
                 conf_dir='/dir/conf',
@@ -192,8 +177,7 @@ class TestXmlConfigResource(TestCase):
                 configuration_attributes={}
                 )
 
-    open_mock.assert_called_with('/dir/conf/file.xml', 'wb')
-    result_file.__enter__().write.assert_called_with(u'<!--Wed 2014-02-->\n    <configuration>\n    \n    <property>\n      <name></name>\n      <value></value>\n    </property>\n    \n    <property>\n      <name>first</name>\n      <value>should be first</value>\n    </property>\n    \n    <property>\n      <name>second</name>\n      <value>should be second</value>\n    </property>\n    \n    <property>\n      <name>third</name>\n      <value>should be third</value>\n    </property>\n    \n    <property>\n      <name>z_last</name>\n      <value>should be last</value>\n    </property>\n    \n  </configuration>\n')
+    create_file_mock.assert_called_with('/dir/conf/file.xml', u'<!--Wed 2014-02-->\n    <configuration>\n    \n    <property>\n      <name></name>\n      <value></value>\n    </property>\n    \n    <property>\n      <name>first</name>\n      <value>should be first</value>\n    </property>\n    \n    <property>\n      <name>second</name>\n      <value>should be second</value>\n    </property>\n    \n    <property>\n      <name>third</name>\n      <value>should be third</value>\n    </property>\n    \n    <property>\n      <name>z_last</name>\n      <value>should be last</value>\n    </property>\n    \n  </configuration>\n')
 
   @patch("resource_management.libraries.providers.xml_config.File")
   @patch.object(os.path, "exists")

+ 2 - 2
ambari-common/src/main/python/resource_management/core/providers/accounts.py

@@ -60,7 +60,7 @@ class UserProvider(Provider):
 
     command.append(self.resource.username)
 
-    shell.checked_call(command)
+    shell.checked_call(command, sudo=True)
 
   def action_remove(self):
     if self.user:
@@ -98,7 +98,7 @@ class GroupProvider(Provider):
         
     command.append(self.resource.group_name)
 
-    shell.checked_call(command)
+    shell.checked_call(command, sudo=True)
 
     group = self.group
 

+ 4 - 4
ambari-common/src/main/python/resource_management/core/providers/package/apt.py

@@ -42,19 +42,19 @@ class AptProvider(PackageProvider):
     if not self._check_existence(name):
       cmd = INSTALL_CMD % (name)
       Logger.info("Installing package %s ('%s')" % (name, cmd))
-      code, out = shell.call(cmd)
+      code, out = shell.call(cmd, sudo=True)
       
       # apt-get update wasn't done too long
       if code:
         Logger.info("Execution of '%s' returned %d. %s" % (cmd, code, out))
         Logger.info("Failed to install package %s. Executing `%s`" % (name, REPO_UPDATE_CMD))
-        code, out = shell.call(REPO_UPDATE_CMD)
+        code, out = shell.call(REPO_UPDATE_CMD, sudo=True)
         
         if code:
           Logger.info("Execution of '%s' returned %d. %s" % (REPO_UPDATE_CMD, code, out))
           
         Logger.info("Retrying to install package %s" % (name))
-        shell.checked_call(cmd)
+        shell.checked_call(cmd, sudo=True)
     else:
       Logger.info("Skipping installing existent package %s" % (name))
 
@@ -67,7 +67,7 @@ class AptProvider(PackageProvider):
     if self._check_existence(name):
       cmd = REMOVE_CMD % (name)
       Logger.info("Removing package %s ('%s')" % (name, cmd))
-      shell.checked_call(cmd)
+      shell.checked_call(cmd, sudo=True)
     else:
       Logger.info("Skipping removing non-existent package %s" % (name))
 

+ 2 - 2
ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py

@@ -33,7 +33,7 @@ class YumProvider(PackageProvider):
     if not self._check_existence(name):
       cmd = INSTALL_CMD % (name)
       Logger.info("Installing package %s ('%s')" % (name, cmd))
-      shell.checked_call(cmd)
+      shell.checked_call(cmd, sudo=True)
     else:
       Logger.info("Skipping installing existent package %s" % (name))
 
@@ -44,7 +44,7 @@ class YumProvider(PackageProvider):
     if self._check_existence(name):
       cmd = REMOVE_CMD % (name)
       Logger.info("Removing package %s ('%s')" % (name, cmd))
-      shell.checked_call(cmd)
+      shell.checked_call(cmd, sudo=True)
     else:
       Logger.info("Skipping removing non-existent package %s" % (name))
 

+ 2 - 2
ambari-common/src/main/python/resource_management/core/providers/package/zypper.py

@@ -33,7 +33,7 @@ class ZypperProvider(PackageProvider):
     if not self._check_existence(name):
       cmd = INSTALL_CMD % (name)
       Logger.info("Installing package %s ('%s')" % (name, cmd))
-      shell.checked_call(cmd)
+      shell.checked_call(cmd, sudo=True)
     else:
       Logger.info("Skipping installing existent package %s" % (name))
 
@@ -44,7 +44,7 @@ class ZypperProvider(PackageProvider):
     if self._check_existence(name):
       cmd = REMOVE_CMD % (name)
       Logger.info("Removing package %s ('%s')" % (name, cmd))
-      shell.checked_call(cmd)
+      shell.checked_call(cmd, sudo=True)
     else:
       Logger.info("Skipping removing non-existent package %s" % (name))
 

+ 37 - 25
ambari-common/src/main/python/resource_management/core/providers/system.py

@@ -28,6 +28,7 @@ import pwd
 import time
 import shutil
 from resource_management.core import shell
+from resource_management.core import sudo
 from resource_management.core.base import Fail
 from resource_management.core import ExecuteTimeoutException
 from resource_management.core.providers import Provider
@@ -58,27 +59,29 @@ def _coerce_gid(group):
 
 def _ensure_metadata(path, user, group, mode=None):
   stat = os.stat(path)
-
+  
   if mode:
     existing_mode = stat.st_mode & 07777
     if existing_mode != mode:
       Logger.info("Changing permission for %s from %o to %o" % (
       path, existing_mode, mode))
-      os.chmod(path, mode)
-
+      sudo.chmod(path, mode)
+  
   if user:
     uid = _coerce_uid(user)
     if stat.st_uid != uid:
       Logger.info(
         "Changing owner for %s from %d to %s" % (path, stat.st_uid, user))
-      os.chown(path, uid, -1)
+      
+      sudo.chown(path, user, None)
+      
 
   if group:
     gid = _coerce_gid(group)
     if stat.st_gid != gid:
       Logger.info(
         "Changing group for %s from %d to %s" % (path, stat.st_gid, group))
-      os.chown(path, -1, gid)
+      sudo.chown(path, None, group)
 
 
 class FileProvider(Provider):
@@ -99,9 +102,8 @@ class FileProvider(Provider):
       reason = "it doesn't exist"
     elif self.resource.replace:
       if content is not None:
-        with open(path, "rb") as fp:
-          old_content = fp.read()
-          old_content = old_content.decode(self.resource.encoding) if self.resource.encoding else old_content
+        old_content = sudo.read_file(path)
+        old_content = old_content.decode(self.resource.encoding) if self.resource.encoding else old_content
         if content != old_content:
           write = True
           reason = "contents don't match"
@@ -110,10 +112,11 @@ class FileProvider(Provider):
 
     if write:
       Logger.info("Writing %s because %s" % (self.resource, reason))
-      with open(path, "wb") as fp:
-        if content:
-          content = content.encode(self.resource.encoding) if self.resource.encoding else content
-          fp.write(content)
+
+      if content:
+        content = content.encode(self.resource.encoding) if self.resource.encoding else content
+        
+      sudo.create_file(path, content)
 
     _ensure_metadata(self.resource.path, self.resource.owner,
                         self.resource.group, mode=self.resource.mode)
@@ -126,7 +129,7 @@ class FileProvider(Provider):
     
     if os.path.exists(path):
       Logger.info("Deleting %s" % self.resource)
-      os.unlink(path)
+      sudo.unlink(path)
 
   def _get_content(self):
     content = self.resource.content
@@ -142,20 +145,21 @@ class FileProvider(Provider):
 class DirectoryProvider(Provider):
   def action_create(self):
     path = self.resource.path
+
     if not os.path.exists(path):
       Logger.info("Creating directory %s" % self.resource)
       if self.resource.recursive:
-        os.makedirs(path, self.resource.mode or 0755)
+        sudo.makedirs(path, self.resource.mode or 0755)
       else:
         dirname = os.path.dirname(path)
         if not os.path.isdir(dirname):
           raise Fail("Applying %s failed, parent directory %s doesn't exist" % (self.resource, dirname))
         
-        os.mkdir(path, self.resource.mode or 0755)
+        sudo.makedir(path, self.resource.mode or 0755)
       
     if not os.path.isdir(path):
       raise Fail("Applying %s failed, file %s already exists" % (self.resource, path))
-
+    
     _ensure_metadata(path, self.resource.owner, self.resource.group,
                         mode=self.resource.mode)
 
@@ -181,7 +185,7 @@ class LinkProvider(Provider):
         raise Fail(
           "%s trying to create a symlink with the same name as an existing file or directory" % self)
       Logger.info("%s replacing old symlink to %s" % (self.resource, oldpath))
-      os.unlink(path)
+      sudo.unlink(path)
       
     if self.resource.hard:
       if not os.path.exists(self.resource.to):
@@ -190,19 +194,19 @@ class LinkProvider(Provider):
         raise Fail("Failed to apply %s, cannot create hard link to a directory (%s)" % (self.resource, self.resource.to))
       
       Logger.info("Creating hard %s" % self.resource)
-      os.link(self.resource.to, path)
+      sudo.link(self.resource.to, path)
     else:
       if not os.path.exists(self.resource.to):
         Logger.info("Warning: linking to nonexistent location %s" % self.resource.to)
         
       Logger.info("Creating symbolic %s" % self.resource)
-      os.symlink(self.resource.to, path)
+      sudo.symlink(self.resource.to, path)
 
   def action_delete(self):
     path = self.resource.path
     if os.path.exists(path):
       Logger.info("Deleting %s" % self.resource)
-      os.unlink(path)
+      sudo.unlink(path)
 
 
 def _preexec_fn(resource):
@@ -223,18 +227,26 @@ class ExecuteProvider(Provider):
 
     Logger.debug("Executing %s" % self.resource)
 
+    env = self.resource.environment
+    
+    # append current PATH, to self.resource.environment['PATH'] and self.resource.path
+    if 'PATH' in env:
+      env['PATH'] = os.pathsep.join([os.environ['PATH'], env['PATH']])
+    if self.resource.path:
+      if not 'PATH' in env:
+        env['PATH'] = ''
+      path = os.pathsep.join(self.resource.path) if isinstance(self.resource.path, (list, tuple)) else self.resource.path
+      env['PATH'] = os.pathsep.join([os.environ['PATH'], path])
+          
     for i in range (0, self.resource.tries):
       try:
-        env=self.resource.environment
-        if env and 'PATH' in env.keys():
-          env['PATH'] = "$PATH" + os.pathsep + env['PATH']
-
         shell.checked_call(self.resource.command, logoutput=self.resource.logoutput,
                             cwd=self.resource.cwd, env=env,
                             preexec_fn=_preexec_fn(self.resource), user=self.resource.user,
                             wait_for_finish=self.resource.wait_for_finish,
                             timeout=self.resource.timeout,
-                            path=self.resource.path)
+                            path=self.resource.path,
+                            sudo=self.resource.sudo)
         break
       except Fail as ex:
         if i == self.resource.tries-1: # last try

+ 2 - 1
ambari-common/src/main/python/resource_management/core/resources/system.py

@@ -76,7 +76,7 @@ class Execute(Resource):
   creates = ResourceArgument()
   cwd = ResourceArgument()
   # this runs command with a specific env variables, env={'JAVA_HOME': '/usr/jdk'}
-  environment = ResourceArgument()
+  environment = ResourceArgument(default={})
   user = ResourceArgument()
   group = ResourceArgument()
   returns = ForcedListArgument(default=0)
@@ -102,6 +102,7 @@ class Execute(Resource):
   - try_sleep
   """
   wait_for_finish = BooleanArgument(default=True)
+  sudo = BooleanArgument(default=False)
 
 
 class ExecuteScript(Resource):

+ 18 - 19
ambari-common/src/main/python/resource_management/core/shell.py

@@ -32,15 +32,15 @@ from exceptions import ExecuteTimeoutException
 from resource_management.core.logger import Logger
 
 def checked_call(command, logoutput=False, 
-         cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, path=None):
-  return _call(command, logoutput, True, cwd, env, preexec_fn, user, wait_for_finish, timeout, path)
+         cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, path=None, sudo=False):
+  return _call(command, logoutput, True, cwd, env, preexec_fn, user, wait_for_finish, timeout, path, sudo)
 
 def call(command, logoutput=False, 
-         cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, path=None):
-  return _call(command, logoutput, False, cwd, env, preexec_fn, user, wait_for_finish, timeout, path)
+         cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, path=None, sudo=False):
+  return _call(command, logoutput, False, cwd, env, preexec_fn, user, wait_for_finish, timeout, path, sudo)
             
 def _call(command, logoutput=False, throw_on_failure=True, 
-         cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, path=None):
+         cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, path=None, sudo=False):
   """
   Execute shell command
   
@@ -55,22 +55,21 @@ def _call(command, logoutput=False, throw_on_failure=True,
   if isinstance(command, (list, tuple)):
     command = ' '.join(quote_bash_args(x) for x in command)
 
-  if path:
-    export_path_command = "export PATH=$PATH" + os.pathsep + os.pathsep.join(path) + " ; "
-  else:
-    export_path_command = ""
-
+  # In case we will use sudo, we have to put all the environment inside the command, 
+  # since Popen environment gets reset within sudo.
+  export_command = reduce(lambda str,x: '{0} {1}={2}'.format(str,x,quote_bash_args(env[x])), env, 'export') + '; ' if env else ''
+      
   if user:
-    if env:
-      export_path_command += "export "
-      for var in env:
-        export_path_command += " " + var + "=" + env[var]
-      export_path_command += " ; "
-
-    subprocess_command = ["su", "-s", "/bin/bash", "-", user, "-c", export_path_command + command]
+    bash_run_command = "/usr/bin/sudo -Hsu {0} <<< {1}".format(quote_bash_args(user), quote_bash_args(export_command + command))
+    # Go to home directory. In case we are in folder, which user cannot open, we might run into troubles with some utils
+    cwd = os.path.expanduser('~'+user) if not cwd and os.path.exists(os.path.expanduser('~'+user)) else cwd
+  elif sudo:
+    bash_run_command = "/usr/bin/sudo -s <<< {0}".format(quote_bash_args(export_command + command))
   else:
-    subprocess_command = ["/bin/bash","--login","-c", export_path_command + command]
-
+    bash_run_command = command
+    
+  subprocess_command = ["/bin/bash","--login","-c", bash_run_command]
+    
   proc = subprocess.Popen(subprocess_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                           cwd=cwd, env=env, shell=False,
                           preexec_fn=preexec_fn)

+ 62 - 0
ambari-common/src/main/python/resource_management/core/sudo.py

@@ -0,0 +1,62 @@
+import os
+import tempfile
+from resource_management.core import shell
+
+# os.chown replacement
+def chown(path, owner, group):
+  if owner:
+    shell.checked_call(["chown", owner, path], sudo=True)
+  if group:
+    shell.checked_call(["chgrp", owner, path], sudo=True)
+    
+# os.chmod replacement
+def chmod(path, mode):
+  shell.checked_call(["chmod", oct(mode), path], sudo=True)
+  
+# os.makedirs replacement
+def makedirs(path, mode):
+  shell.checked_call(["mkdir", "-p", path], sudo=True)
+  chmod(path, mode)
+  
+# os.makedir replacement
+def makedir(path, mode):
+  shell.checked_call(["mkdir", path], sudo=True)
+  chmod(path, mode)
+  
+# os.symlink replacement
+def symlink(source, link_name):
+  shell.checked_call(["ln","-sf", source, link_name], sudo=True)
+  
+# os.link replacement
+def link(source, link_name):
+  shell.checked_call(["ln", "-f", source, link_name], sudo=True)
+  
+# os unlink
+def unlink(path):
+  shell.checked_call(["rm","-f", path], sudo=True)
+  
+# fp.write replacement
+def create_file(filename, content):
+  """
+  if content is None, create empty file
+  """
+  tmpf = tempfile.NamedTemporaryFile()
+  
+  if content:
+    with open(tmpf.name, "wb") as fp:
+      fp.write(content)
+  
+  with tmpf:    
+    shell.checked_call(["cp", "-f", tmpf.name, filename], sudo=True)
+    
+  # set default files mode
+  chmod(filename, 0644)
+    
+# fp.read replacement
+def read_file(filename):
+  tmpf = tempfile.NamedTemporaryFile()
+  shell.checked_call(["cp", "-f", filename, tmpf.name], sudo=True)
+  
+  with tmpf:
+    with open(tmpf.name, "rb") as fp:
+      return fp.read()

+ 2 - 2
ambari-common/src/main/python/resource_management/libraries/providers/hdfs_directory.py

@@ -104,8 +104,8 @@ class HdfsDirectoryProvider(Provider):
                    chown_cmd=' && '.join(chown_commands)),
             user=hdp_hdfs_user,
             path=bin_dir,
-            not_if=format("su - {hdp_hdfs_user} -c 'export PATH=$PATH:{bin_dir} ; "
-                          "hadoop --config {hdp_conf_dir} fs -ls {dir_list_str}'")
+            not_if=format("sudo -Hsu {hdp_hdfs_user} <<< "
+                          "'export PATH=$PATH:{bin_dir} ; hadoop --config {hdp_conf_dir} fs -ls {dir_list_str}'")
     )
 
     directories_list[:] = []

+ 0 - 3
ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java

@@ -95,9 +95,6 @@ public class SshHostInfo {
   }
   
   public String getUserRunAs() {
-    // TODO: remove this once UI supports customizing ambari run-as-user
-    if(userRunAs == null)
-      return "root";
     return userRunAs;
   }
 

+ 2 - 2
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HIVE/package/scripts/hive_service.py

@@ -21,7 +21,7 @@ limitations under the License.
 from resource_management import *
 import sys
 import time
-from resource_management.core.shell import call
+from resource_management.core import shell
 
 def hive_service(
     name,
@@ -98,7 +98,7 @@ def check_fs_root():
   import params  
   fs_root_url = format("{fs_root}{hive_apps_whs_dir}")
   cmd = format("metatool -listFSRoot 2>/dev/null | grep hdfs:// | grep -v '.db$'")
-  code, out = call(cmd, user=params.hive_user)
+  code, out = shell.call(cmd, user=params.hive_user)
   if code == 0 and fs_root_url.strip() != out.strip():
     cmd = format("metatool -updateLocation {fs_root}{hive_apps_whs_dir} {out}")
     Execute(cmd,

+ 6 - 6
ambari-server/src/test/python/stacks/2.0.6/HIVE/test_hive_server.py

@@ -20,6 +20,7 @@ limitations under the License.
 import os
 import subprocess
 from mock.mock import MagicMock, call, patch
+from resource_management.core import shell
 from stacks.utils.RMFTestCase import *
 
 import socket
@@ -35,6 +36,7 @@ class TestHiveServer(RMFTestCase):
     self.assert_configure_default()
     self.assertNoMoreResources()
 
+  @patch.object(shell, "call", new=MagicMock(return_value=(0, '')))
   @patch.object(subprocess,"Popen")
   @patch("socket.socket")
   def test_start_default(self, socket_mock, popen_mock):
@@ -47,7 +49,10 @@ class TestHiveServer(RMFTestCase):
     )
 
     self.assert_configure_default()
-
+    self.assertResourceCalled('Execute', 'metatool -updateLocation hdfs://c6401.ambari.apache.org:8020/apps/hive/warehouse ',
+        environment = {'PATH' : os.environ['PATH'] + os.pathsep + "/usr/lib/hive/bin" + os.pathsep + "/usr/bin"},
+        user = 'hive',
+    )
     self.assertResourceCalled('Execute', 'env JAVA_HOME=/usr/jdk64/jdk1.7.0_45 /tmp/start_hiveserver2_script /var/log/hive/hive-server2.out /var/log/hive/hive-server2.log /var/run/hive/hive-server.pid /etc/hive/conf.server /var/log/hive',
                               not_if = 'ls /var/run/hive/hive-server.pid >/dev/null 2>&1 && ps -p `cat /var/run/hive/hive-server.pid` >/dev/null 2>&1',
                               environment = {'HADOOP_HOME' : '/usr'},
@@ -60,11 +65,6 @@ class TestHiveServer(RMFTestCase):
     )
 
     self.assertNoMoreResources()
-    self.assertTrue(popen_mock.called)
-    popen_mock.assert_called_with(
-      ['su', '-s', '/bin/bash', '-', u'hive', '-c', "metatool -listFSRoot 2>/dev/null | grep hdfs:// | grep -v '.db$'"],
-      shell=False, preexec_fn=None, stderr=-2, stdout=-1, env=None, cwd=None
-    )
     self.assertTrue(socket_mock.called)
     self.assertTrue(s.close.called)