You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/03/03 03:06:23 UTC

[3/3] mesos git commit: Added test for Appc image fetcher.

Added test for Appc image fetcher.

Added simple appc Fetcher test with mock HTTP image server.

Review: https://reviews.apache.org/r/43969/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f0fb4046
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f0fb4046
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f0fb4046

Branch: refs/heads/master
Commit: f0fb404616d7af58083396b880a2b14abb7e671b
Parents: 6903c6e
Author: Jojy Varghese <jo...@mesosphere.io>
Authored: Wed Mar 2 18:04:15 2016 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Mar 2 18:04:15 2016 -0800

----------------------------------------------------------------------
 .../containerizer/provisioner_appc_tests.cpp    | 285 ++++++++++++++++---
 1 file changed, 252 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f0fb4046/src/tests/containerizer/provisioner_appc_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/provisioner_appc_tests.cpp b/src/tests/containerizer/provisioner_appc_tests.cpp
index 9d9779a..86e1964 100644
--- a/src/tests/containerizer/provisioner_appc_tests.cpp
+++ b/src/tests/containerizer/provisioner_appc_tests.cpp
@@ -16,6 +16,8 @@
 
 #include <string>
 
+#include <gmock/gmock.h>
+
 #include <mesos/slave/isolator.hpp>
 
 #include <process/gtest.hpp>
@@ -31,11 +33,14 @@
 
 #include <mesos/appc/spec.hpp>
 
+#include "common/command_utils.hpp"
+
 #include "slave/paths.hpp"
 
 #include "slave/containerizer/mesos/provisioner/paths.hpp"
 #include "slave/containerizer/mesos/provisioner/provisioner.hpp"
 
+#include "slave/containerizer/mesos/provisioner/appc/fetcher.hpp"
 #include "slave/containerizer/mesos/provisioner/appc/paths.hpp"
 #include "slave/containerizer/mesos/provisioner/appc/store.hpp"
 
@@ -43,6 +48,9 @@ using std::list;
 using std::string;
 using std::vector;
 
+using testing::_;
+using testing::Return;
+
 using namespace process;
 
 using namespace mesos::internal::slave::appc;
@@ -133,8 +141,11 @@ protected:
   // directory. The directory structure reflects the Appc image spec.
   //
   // @param storeDir Path to the store directory where all images are stored.
+  // @param manifest Manifest JSON to be used for the test image.
   // @return  Path to the test image directory.
-  Try<string> createTestImage(const string& storeDir)
+  Try<string> createTestImage(
+      const string& storeDir,
+      const JSON::Value& manifest)
   {
     Try<Nothing> mkdir = os::mkdir(storeDir, true);
     if (mkdir.isError()) {
@@ -147,32 +158,6 @@ protected:
     //    |--<id>
     //       |--manifest
     //       |--rootfs/tmp/test
-    JSON::Value manifest = JSON::parse(
-        "{"
-        "  \"acKind\": \"ImageManifest\","
-        "  \"acVersion\": \"0.6.1\","
-        "  \"name\": \"foo.com/bar\","
-        "  \"labels\": ["
-        "    {"
-        "      \"name\": \"version\","
-        "      \"value\": \"1.0.0\""
-        "    },"
-        "    {"
-        "      \"name\": \"arch\","
-        "      \"value\": \"amd64\""
-        "    },"
-        "    {"
-        "      \"name\": \"os\","
-        "      \"value\": \"linux\""
-        "    }"
-        "  ],"
-        "  \"annotations\": ["
-        "    {"
-        "      \"name\": \"created\","
-        "      \"value\": \"1438983392\""
-        "    }"
-        "  ]"
-        "}").get();
 
     // The 'imageId' below has the correct format but it's not computed
     // by hashing the tarball of the image. It's OK here as we assume
@@ -247,6 +232,39 @@ protected:
 
     return appc;
   }
+
+  // Abstracts the manifest accessor for the test fixture. This provides the
+  // ability for customizing manifests for fixtures.
+  virtual JSON::Value getManifest() const
+  {
+    return JSON::parse(
+        R"~(
+        {
+          "acKind": "ImageManifest",
+          "acVersion": "0.6.1",
+          "name": "foo.com/bar",
+          "labels": [
+            {
+              "name": "version",
+              "value": "1.0.0"
+            },
+            {
+              "name": "arch",
+              "value": "amd64"
+            },
+            {
+              "name": "os",
+              "value": "linux"
+            }
+          ],
+          "annotations": [
+            {
+              "name": "created",
+              "value": "1438983392"
+            }
+          ]
+        })~").get();
+  }
 };
 
 
@@ -258,7 +276,9 @@ TEST_F(AppcStoreTest, Recover)
   Try<Owned<slave::Store>> store = Store::create(flags);
   ASSERT_SOME(store);
 
-  Try<string> createImage = createTestImage(flags.appc_store_dir);
+  Try<string> createImage = createTestImage(
+      flags.appc_store_dir,
+      getManifest());
 
   ASSERT_SOME(createImage);
 
@@ -296,12 +316,12 @@ TEST_F(ProvisionerAppcTest, ROOT_Provision)
   flags.image_provisioner_backend = "bind";
   flags.work_dir = "work_dir";
 
-  Fetcher fetcher;
-
   Try<Owned<Provisioner>> provisioner = Provisioner::create(flags);
   ASSERT_SOME(provisioner);
 
-  Try<string> createImage = createTestImage(flags.appc_store_dir);
+  Try<string> createImage = createTestImage(
+      flags.appc_store_dir,
+      getManifest());
 
   ASSERT_SOME(createImage);
 
@@ -363,11 +383,12 @@ TEST_F(ProvisionerAppcTest, Recover)
   flags.image_provisioner_backend = "copy";
   flags.work_dir = "work_dir";
 
-  Fetcher fetcher;
   Try<Owned<Provisioner>> provisioner1 = Provisioner::create(flags);
   ASSERT_SOME(provisioner1);
 
-  Try<string> createImage = createTestImage(flags.appc_store_dir);
+  Try<string> createImage = createTestImage(
+      flags.appc_store_dir,
+      getManifest());
 
   ASSERT_SOME(createImage);
 
@@ -428,6 +449,204 @@ TEST_F(ProvisionerAppcTest, Recover)
   EXPECT_FALSE(os::exists(containerDir));
 }
 
+
+// Mock HTTP image server.
+class TestAppcImageServer : public Process<TestAppcImageServer>
+{
+public:
+  TestAppcImageServer() : ProcessBase("TestAppcImageServer") {}
+
+  void addRoute(const string& imageName)
+  {
+    route("/" + imageName, None(), &TestAppcImageServer::serve);
+  }
+
+  MOCK_METHOD1(serve, Future<http::Response>(const http::Request&));
+};
+
+
+// Test fixture that uses the mock HTTP image server. This fixture provides the
+// abstraction for creating and composing complex test cases for Appc image
+// fetcher and store.
+class AppcImageFetcherTest : public AppcStoreTest
+{
+protected:
+  // Custom implementation that overrides the host and port of the image name.
+  JSON::Value getManifest() const
+  {
+    string imageName = strings::format(
+        "%s:%d/TestAppcImageServer/image",
+        stringify(server.self().address.ip),
+        server.self().address.port).get();
+
+    return JSON::parse(
+        R"~(
+        {
+          "acKind": "ImageManifest",
+          "acVersion": "0.6.1",
+          "name": " + imageName + ",
+          "labels": [
+            {
+              "name": "version",
+              "value": "1.0.0"
+            },
+            {
+              "name": "arch",
+              "value": "amd64"
+            },
+            {
+              "name": "os",
+              "value": "linux"
+            }
+          ],
+          "annotations": [
+            {
+              "name": "created",
+              "value": "1438983392"
+            }
+          ]
+        })~").get();
+  }
+
+  // TODO(jojy): Currently only supports serving one image. Consider adding
+  // support serving any image on the server. One way to do this is to add a map
+  // of image name -> server path for each image.
+  Future<http::Response> serveImage()
+  {
+    http::OK response;
+
+    response.type = response.PATH;
+    response.path = imageBundlePath;
+    response.headers["Content-Type"] = "application/octet-stream";
+    response.headers["Content-Disposition"] = strings::format(
+        "attachment; filename=%s",
+        imageBundlePath).get();
+
+    return response;
+  }
+
+  // TODO(jojy): Currently just uses 'imageId' to prepare a image on
+  // the server. Consider adding more parameters(e.g, 'labels').
+  void prepareServerImage(const string& fileName, const string& imageId)
+  {
+    const Path serverDir(path::join(os::getcwd(), "server"));
+
+    Try<string> createImage = createTestImage(serverDir, getManifest());
+    ASSERT_SOME(createImage);
+
+    // Set image file path for the test.
+    imageBundlePath = path::join(serverDir, fileName);
+
+    const Path imageDir(path::join(serverDir, "images", imageId));
+
+    Future<Nothing> future = command::tar(
+        Path("."),
+        Path(imageBundlePath),
+        imageDir,
+        command::Compression::GZIP);
+
+    AWAIT_READY(future);
+
+    // Now add route on the server for the image.
+    server.addRoute(fileName);
+  }
+
+  virtual void SetUp()
+  {
+    TemporaryDirectoryTest::SetUp();
+
+    // Now spawn the image server.
+    spawn(server);
+  }
+
+  virtual void TearDown()
+  {
+    terminate(server);
+    wait(server);
+
+    TemporaryDirectoryTest::TearDown();
+  }
+
+  string imageBundlePath;
+  TestAppcImageServer server;
+};
+
+
+// Tests simple fetch functionality of the appc::Fetcher component.
+// The test fetches a test Appc image from the http server and
+// verifies its content. The image is served in 'tar + gzip' format.
+TEST_F(AppcImageFetcherTest, SimpleFetch)
+{
+  // Setup the image on the image server.
+  prepareServerImage(
+      "image-latest-linux-amd64.aci",
+      "sha512-e77d96aa0240eedf134b8c90baeaf76dca8e78691836301d7498c8402044604"
+      "2e797b296d6ab296e0954c2626bfb264322ebeb8f447dac4fac6511ea06bc61f0");
+
+  // Setup server.
+  EXPECT_CALL(server, serve(_))
+    .WillOnce(Return(serveImage()));
+
+  // Appc Image to be fetched.
+  const string imageName =
+    stringify(server.self().address) + "/TestAppcImageServer/image";
+
+  Image::Appc imageInfo;
+  imageInfo.set_name(imageName);
+
+  Label archLabel;
+  archLabel.set_key("arch");
+  archLabel.set_value("amd64");
+
+  Label osLabel;
+  osLabel.set_key("os");
+  osLabel.set_value("linux");
+
+  Labels labels;
+  labels.add_labels()->CopyFrom(archLabel);
+  labels.add_labels()->CopyFrom(osLabel);
+
+  imageInfo.mutable_labels()->CopyFrom(labels);
+
+  // Create image fetcher.
+  Try<Owned<uri::Fetcher>> uriFetcher = uri::fetcher::create();
+  ASSERT_SOME(uriFetcher);
+
+  slave::Flags flags;
+
+  Try<Owned<slave::appc::Fetcher>> fetcher =
+    slave::appc::Fetcher::create(flags, uriFetcher.get().share());
+
+  ASSERT_SOME(fetcher);
+
+  // Prepare fetch directory.
+  const Path imageFetchDir(path::join(os::getcwd(), "fetched-images"));
+
+  Try<Nothing> mkdir = os::mkdir(imageFetchDir);
+  ASSERT_SOME(mkdir);
+
+  // Now fetch the image.
+  AWAIT_READY(fetcher.get()->fetch(imageInfo, imageFetchDir));
+
+  // Verify that there is an image directory.
+  Try<list<string>> imageDirs = os::ls(imageFetchDir);
+  ASSERT_SOME(imageDirs);
+
+  // Verify that there is only ONE image directory.
+  ASSERT_EQ(1u, imageDirs.get().size());
+
+  // Verify that there is a roofs.
+  const Path imageRootfs(path::join(
+      imageFetchDir,
+      imageDirs.get().front(),
+      "rootfs"));
+
+  ASSERT_TRUE(os::exists(imageRootfs));
+
+  // Verify that the image fetched is the same as on the server.
+  ASSERT_SOME_EQ("test", os::read(path::join(imageRootfs, "tmp", "test")));
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {