build_dlc: prepare /etc/update_engine.conf

/etc/update_engine.conf is a file that identifies the supported minor and major
versions of the update_engine client. delta_generator requires this file in the
image to generate a correct delta payload.

This patch adds the flag --build-dir-root that normally SYSROOT is
passed to it. We use this directory to extract /etc/update_engine.conf and
other possible resource we need.

BUG=chromium:926986
TEST=unittest
TEST=emerge-edgar dummy-dlc, then looked in the file and the config was present.
CQ-DEPEND=CL:1449892

Change-Id: I9a369059be5308801b3aeca9881cb218307f03ee
Reviewed-on: https://chromium-review.googlesource.com/1446595
Commit-Ready: Amin Hassani <ahassani@chromium.org>
Tested-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Nicolas Norvez <norvez@chromium.org>
diff --git a/scripts/build_dlc.py b/scripts/build_dlc.py
index 2b11e15..1b713cc 100644
--- a/scripts/build_dlc.py
+++ b/scripts/build_dlc.py
@@ -11,6 +11,7 @@
 import json
 import math
 import os
+import shutil
 
 from chromite.lib import commandline
 from chromite.lib import cros_build_lib
@@ -21,6 +22,14 @@
 DLC_IMAGE_DIR = 'build/rootfs/dlc/'
 LSB_RELEASE = 'etc/lsb-release'
 
+# This file has major and minor version numbers that the update_engine client
+# supports. These values are needed for generating a delta/full payload.
+UPDATE_ENGINE_CONF = 'etc/update_engine.conf'
+
+_EXTRA_RESOURCES = (
+    UPDATE_ENGINE_CONF,
+)
+
 DLC_ID_KEY = 'DLC_ID'
 DLC_NAME_KEY = 'DLC_NAME'
 
@@ -59,12 +68,13 @@
   # The DLC root path inside the DLC module.
   _DLC_ROOT_DIR = 'root'
 
-  def __init__(self, src_dir, install_root_dir, fs_type, pre_allocated_blocks,
-               version, dlc_id, name):
+  def __init__(self, src_dir, build_root_dir, install_root_dir, fs_type,
+               pre_allocated_blocks, version, dlc_id, name):
     """Object initializer.
 
     Args:
       src_dir: (str) path to the DLC source root directory.
+      build_root_dir: (str) The path to the build root directory.
       install_root_dir: (str) The path to the root installation directory.
       fs_type: (str) file system type.
       pre_allocated_blocks: (int) number of blocks pre-allocated on device.
@@ -73,6 +83,7 @@
       name: (str) DLC name.
     """
     self.src_dir = src_dir
+    self.build_root_dir = build_root_dir
     self.install_root_dir = install_root_dir
     self.fs_type = fs_type
     self.pre_allocated_blocks = pre_allocated_blocks
@@ -119,14 +130,8 @@
       # Mount the ext4 image.
       osutils.MountDir(self.dest_image, mount_point, mount_opts=('loop', 'rw'))
 
-      dlc_root_dir = os.path.join(mount_point, self._DLC_ROOT_DIR)
-      osutils.SafeMakedirs(dlc_root_dir)
       try:
-        # Copy DLC files over to the image.
-        osutils.CopyDirContents(self.src_dir, dlc_root_dir)
-        self.PrepareLsbRelease(mount_point)
-
-        self.SquashOwnerships(mount_point)
+        self.SetupDlcImageFiles(mount_point)
       finally:
         # Unmount the ext4 image.
         osutils.UmountDir(mount_point)
@@ -140,12 +145,7 @@
     """Create a squashfs image."""
     with osutils.TempDir(prefix='dlc_') as temp_dir:
       squashfs_root = os.path.join(temp_dir, 'squashfs-root')
-      dlc_root_dir = os.path.join(squashfs_root, self._DLC_ROOT_DIR)
-      osutils.SafeMakedirs(dlc_root_dir)
-      osutils.CopyDirContents(self.src_dir, dlc_root_dir)
-      self.PrepareLsbRelease(squashfs_root)
-
-      self.SquashOwnerships(squashfs_root)
+      self.SetupDlcImageFiles(squashfs_root)
 
       cros_build_lib.RunCommand(['mksquashfs', squashfs_root, self.dest_image,
                                  '-4k-align', '-noappend'],
@@ -155,6 +155,19 @@
       # directory. Now we need to remove it manually.
       osutils.RmDir(squashfs_root, sudo=True)
 
+  def SetupDlcImageFiles(self, dlc_dir):
+    """Prepares the directory dlc_dir with all the files a DLC needs.
+
+    Args:
+      dlc_dir: (str) The path to where to setup files inside the DLC.
+    """
+    dlc_root_dir = os.path.join(dlc_dir, self._DLC_ROOT_DIR)
+    osutils.SafeMakedirs(dlc_root_dir)
+    osutils.CopyDirContents(self.src_dir, dlc_root_dir)
+    self.PrepareLsbRelease(dlc_dir)
+    self.CollectExtraResources(dlc_dir)
+    self.SquashOwnerships(dlc_dir)
+
   def PrepareLsbRelease(self, dlc_dir):
     """Prepare the file /etc/lsb-release in the DLC module.
 
@@ -174,6 +187,21 @@
     content = ''.join(['%s=%s\n' % (k, v) for k, v in fields.items()])
     osutils.WriteFile(lsb_release, content)
 
+  def CollectExtraResources(self, dlc_dir):
+    """Collect the extra resources needed by the DLC module.
+
+    Look at the documentation around _EXTRA_RESOURCES.
+
+    Args:
+      dlc_dir: (str) The path to root directory of the DLC. e.g. mounted point
+          when we are creating the image.
+    """
+    for r in _EXTRA_RESOURCES:
+      source_path = os.path.join(self.build_root_dir, r)
+      target_path = os.path.join(dlc_dir, r)
+      osutils.SafeMakedirs(os.path.dirname(target_path))
+      shutil.copyfile(source_path, target_path)
+
   def CreateImage(self):
     """Create the image and copy the DLC files to it."""
     if self.fs_type == _EXT4_TYPE:
@@ -254,6 +282,10 @@
                         required=True,
                         help='Root directory path that contains all DLC files '
                         'to be packed.')
+  required.add_argument('--build-root-dir', type='path', metavar='DIR',
+                        required=True,
+                        help="The root path to the board's build root, e.g. "
+                        "/build/eve")
   required.add_argument('--install-root-dir', type='path', metavar='DIR',
                         required=True,
                         help='The root path to install DLC images (in %s) and '
@@ -285,7 +317,12 @@
     raise Exception('ext4 unsupported, see https://crbug.com/890060')
 
   # Generate final DLC files.
-  dlc_generator = DlcGenerator(opts.src_dir, opts.install_root_dir,
-                               opts.fs_type, opts.pre_allocated_blocks,
-                               opts.version, opts.id, opts.name)
+  dlc_generator = DlcGenerator(src_dir=opts.src_dir,
+                               build_root_dir=opts.build_root_dir,
+                               install_root_dir=opts.install_root_dir,
+                               fs_type=opts.fs_type,
+                               pre_allocated_blocks=opts.pre_allocated_blocks,
+                               version=opts.version,
+                               dlc_id=opts.id,
+                               name=opts.name)
   dlc_generator.GenerateDLC()
diff --git a/scripts/build_dlc_unittest.py b/scripts/build_dlc_unittest.py
index eb262b1..5dad40c 100644
--- a/scripts/build_dlc_unittest.py
+++ b/scripts/build_dlc_unittest.py
@@ -47,8 +47,19 @@
     """Factory method for a DcGenerator object"""
     src_dir = os.path.join(self.tempdir, 'src')
     osutils.SafeMakedirs(src_dir)
-    return build_dlc.DlcGenerator(src_dir, self.tempdir, fs_type,
-                                  _PRE_ALLOCATED_BLOCKS, _VERSION, _ID, _NAME)
+
+    build_root_dir = os.path.join(self.tempdir, 'build_root')
+    ue_conf = os.path.join(build_root_dir, 'etc', 'update_engine.conf')
+    osutils.WriteFile(ue_conf, 'foo-content', makedirs=True)
+
+    return build_dlc.DlcGenerator(src_dir=src_dir,
+                                  build_root_dir=build_root_dir,
+                                  install_root_dir=self.tempdir,
+                                  fs_type=fs_type,
+                                  pre_allocated_blocks=_PRE_ALLOCATED_BLOCKS,
+                                  version=_VERSION,
+                                  dlc_id=_ID,
+                                  name=_NAME)
 
   def testSetInstallDir(self):
     """Tests install_root_dir is used correclty."""
@@ -101,6 +112,18 @@
     self.assertEqual(osutils.ReadFile(os.path.join(dlc_dir, 'etc/lsb-release')),
                      'DLC_ID=%s\nDLC_NAME=%s\n' %  (_ID, _NAME))
 
+  def testCollectExtraResources(self):
+    """Tests that extra resources are collected correctly."""
+    generator = self.GetDlcGenerator()
+
+    dlc_dir = os.path.join(self.tempdir, 'dlc_dir')
+    generator.CollectExtraResources(dlc_dir)
+
+    ue_conf = 'etc/update_engine.conf'
+    self.assertEqual(
+        osutils.ReadFile(os.path.join(self.tempdir, 'build_root', ue_conf)),
+        'foo-content')
+
   def testGetImageloaderJsonContent(self):
     """Test that GetImageloaderJsonContent returns correct content."""
     blocks = 100