Repository: Azure/WALinuxAgent

Issue:
[2.1.5]waagent doesn't delete /etc/resolv.conf during deprovisioning
Description of problem:
After deprovisioning, the /etc/resolv.conf is not deleted.

Version-Release number of selected component (if applicable):
WALinuxAgent-2.1.5

RHEL Version:
RHEL-7.3 internal build

How reproducible:
100%

Steps to Reproduce:
1. Prepare a RHEL7.3 VM on Azure. Check /etc/resolv.conf:
\# ls -l /etc/resolv.conf
-rw-r--r--. 1 root root 125 Aug  8 10:18 /etc/resolv.conf
2. Deprovision the VM:
\# waagent -deprovision and input 'y' to execute deprovisioning.
3. Check the messages:
WARNING! The waagent service will be stopped.
WARNING! All SSH host key pairs will be deleted.
WARNING! Cached DHCP leases will be deleted.
WARNING! root password will be disabled. You will not be able to login as root.
Do you want to proceed (y/n)y
4. Check the /etc/resolv.conf
\# ls -l /etc/resolv.conf
-rw-r--r--. 1 root root 125 Aug  8 10:18 /etc/resolv.conf

Actual results:
There's no WARNING message to show that /etc/resolv.conf file will be deleted.
The /etc/resolv.conf file is not deleted after deprovisioning.

Expected results:
There's a WARNING message to show that /etc/resolv.conf file will be deleted.
The /etc/resolv.conf file is deleted after deprovisioning.

Additional info:
1. In WALinuxAgent-2.0.16 the /etc/resolv.conf is deleted during deprovisioning.
2. It seems that there's no code to delete /etc/resolv.conf in azurelinuxagent/pa/deprovision/default.py.


Discussion:

Patch:
diff --git a/azurelinuxagent/pa/deprovision/default.py b/azurelinuxagent/pa/deprovision/default.py
index b570c318..3a916e21 100644
--- a/azurelinuxagent/pa/deprovision/default.py
+++ b/azurelinuxagent/pa/deprovision/default.py
@@ -74,6 +74,11 @@ class DeprovisionHandler(object):
         files_to_del = ['/root/.bash_history', '/var/log/waagent.log']
         actions.append(DeprovisionAction(fileutil.rm_files, files_to_del))
 
+    def del_resolv(self, warnings, actions):
+        warnings.append("WARNING! /etc/resolv.conf will be deleted.")
+        files_to_del = ["/etc/resolv.conf"]
+        actions.append(DeprovisionAction(fileutil.rm_files, files_to_del))
+
     def del_dhcp_lease(self, warnings, actions):
         warnings.append("WARNING! Cached DHCP leases will be deleted.")
         dirs_to_del = ["/var/lib/dhclient", "/var/lib/dhcpcd", "/var/lib/dhcp"]
@@ -109,6 +114,7 @@ class DeprovisionHandler(object):
 
         self.del_lib_dir(warnings, actions)
         self.del_files(warnings, actions)
+        self.del_resolv(warnings, actions)
 
         if deluser:
             self.del_user(warnings, actions)
diff --git a/azurelinuxagent/pa/deprovision/ubuntu.py b/azurelinuxagent/pa/deprovision/ubuntu.py
index 14f90de0..b45d4154 100644
--- a/azurelinuxagent/pa/deprovision/ubuntu.py
+++ b/azurelinuxagent/pa/deprovision/ubuntu.py
@@ -18,30 +18,25 @@
 #
 
 import os
-import azurelinuxagent.common.logger as logger
 import azurelinuxagent.common.utils.fileutil as fileutil
 from azurelinuxagent.pa.deprovision.default import DeprovisionHandler, \
-                                                   DeprovisionAction
-
-def del_resolv():
-    if os.path.realpath('/etc/resolv.conf') != '/run/resolvconf/resolv.conf':
-        logger.info("resolvconf is not configured. Removing /etc/resolv.conf")
-        fileutil.rm_files('/etc/resolv.conf')
-    else:
-        logger.info("resolvconf is enabled; leaving /etc/resolv.conf intact")
-        fileutil.rm_files('/etc/resolvconf/resolv.conf.d/tail',
-                             '/etc/resolvconf/resolv.conf.d/originial')
+    DeprovisionAction
 
 
 class UbuntuDeprovisionHandler(DeprovisionHandler):
     def __init__(self):
         super(UbuntuDeprovisionHandler, self).__init__()
 
-    def setup(self, deluser):
-        warnings, actions = super(UbuntuDeprovisionHandler, self).setup(deluser)
-        warnings.append("WARNING! Nameserver configuration in "
-                        "/etc/resolvconf/resolv.conf.d/{tail,originial} "
-                        "will be deleted.")
-        actions.append(DeprovisionAction(del_resolv))
-        return warnings, actions
-
+    def del_resolv(self, warnings, actions):
+        if os.path.realpath(
+                '/etc/resolv.conf') != '/run/resolvconf/resolv.conf':
+            warnings.append("WARNING! /etc/resolv.conf will be deleted.")
+            files_to_del = ["/etc/resolv.conf"]
+            actions.append(DeprovisionAction(fileutil.rm_files, files_to_del))
+        else:
+            warnings.append("WARNING! /etc/resolvconf/resolv.conf.d/tail "
+                            "and /etc/resolvconf/resolv.conf.d/original will "
+                            "be deleted.")
+            files_to_del = ["/etc/resolvconf/resolv.conf.d/tail",
+                            "/etc/resolvconf/resolv.conf.d/original"]
+            actions.append(DeprovisionAction(fileutil.rm_files, files_to_del))


Test patch:
diff --git a/tests/pa/test_deprovision.py b/tests/pa/test_deprovision.py
new file mode 100644
index 00000000..be349153
--- /dev/null
+++ b/tests/pa/test_deprovision.py
@@ -0,0 +1,48 @@
+# Copyright 2016 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+from tests.tools import *
+from azurelinuxagent.pa.deprovision import get_deprovision_handler
+
+
+class TestDeprovision(AgentTestCase):
+    @distros("redhat")
+    def test_deprovision(self,
+                         distro_name,
+                         distro_version,
+                         distro_full_name):
+        deprovision_handler = get_deprovision_handler(distro_name,
+                                                      distro_version,
+                                                      distro_full_name)
+        warnings, actions = deprovision_handler.setup(deluser=False)
+        assert any("/etc/resolv.conf" in w for w in warnings)
+
+    @distros("ubuntu")
+    def test_deprovision_ubuntu(self,
+                                distro_name,
+                                distro_version,
+                                distro_full_name):
+        deprovision_handler = get_deprovision_handler(distro_name,
+                                                      distro_version,
+                                                      distro_full_name)
+
+        with patch("os.path.realpath", return_value="/run/resolvconf/resolv.conf"):
+            warnings, actions = deprovision_handler.setup(deluser=False)
+            assert any("/etc/resolvconf/resolv.conf.d/tail" in w for w in warnings)
+
+if __name__ == '__main__':
+    unittest.main()