Added Appc spec validation utility.

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


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

Branch: refs/heads/master
Commit: ab75d1d662e1506550f277e6403d1bd25aa060d4
Parents: a070de3
Author: Jiang Yan Xu <[email protected]>
Authored: Mon Aug 10 11:15:46 2015 -0700
Committer: Jiang Yan Xu <[email protected]>
Committed: Thu Aug 13 15:16:22 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |   3 +
 .../containerizer/provisioners/appc/spec.cpp    | 104 +++++++++++++++++
 .../containerizer/provisioners/appc/spec.hpp    |  54 +++++++++
 .../containerizer/appc_provisioner_tests.cpp    | 114 +++++++++++++++++++
 4 files changed, 275 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ab75d1d6/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index f8e54d2..d664df6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -458,6 +458,7 @@ libmesos_no_3rdparty_la_SOURCES =                           
        \
        slave/containerizer/mesos/launch.cpp                            \
        slave/containerizer/provisioner.cpp                             \
        slave/containerizer/provisioners/appc/paths.cpp                 \
+       slave/containerizer/provisioners/appc/spec.cpp                  \
        slave/resource_estimators/noop.cpp                              \
        usage/usage.cpp                                                 \
        v1/attributes.cpp                                               \
@@ -713,6 +714,7 @@ libmesos_no_3rdparty_la_SOURCES +=                          
        \
        slave/containerizer/linux_launcher.hpp                          \
        slave/containerizer/provisioner.hpp                             \
        slave/containerizer/provisioners/appc/paths.hpp                 \
+       slave/containerizer/provisioners/appc/spec.hpp                  \
        slave/containerizer/isolators/posix.hpp                         \
        slave/containerizer/isolators/posix/disk.hpp                    \
        slave/containerizer/isolators/cgroups/constants.hpp             \
@@ -1619,6 +1621,7 @@ mesos_tests_SOURCES =                                     
        \
   tests/zookeeper_url_tests.cpp                                        \
   tests/common/http_tests.cpp                                  \
   tests/common/recordio_tests.cpp                              \
+  tests/containerizer/appc_provisioner_tests.cpp               \
   tests/containerizer/composing_containerizer_tests.cpp                \
   tests/containerizer/docker_containerizer_tests.cpp           \
   tests/containerizer/docker_tests.cpp                         \

http://git-wip-us.apache.org/repos/asf/mesos/blob/ab75d1d6/src/slave/containerizer/provisioners/appc/spec.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc/spec.cpp 
b/src/slave/containerizer/provisioners/appc/spec.cpp
new file mode 100644
index 0000000..15a3257
--- /dev/null
+++ b/src/slave/containerizer/provisioners/appc/spec.cpp
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <stout/os/stat.hpp>
+#include <stout/protobuf.hpp>
+#include <stout/strings.hpp>
+
+#include "slave/containerizer/provisioners/appc/paths.hpp"
+#include "slave/containerizer/provisioners/appc/spec.hpp"
+
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace appc {
+namespace spec {
+
+Option<Error> validateManifest(const AppcImageManifest& manifest)
+{
+  // TODO(idownes): Validate that required fields are present when
+  // this cannot be expressed in the protobuf specification, e.g.,
+  // repeated fields with >= 1.
+  // TODO(xujyan): More thorough type validation:
+  // https://github.com/appc/spec/blob/master/spec/types.md
+  if (manifest.ackind() != "ImageManifest") {
+    return Error("Incorrect acKind field: " + manifest.ackind());
+  }
+
+  return None();
+}
+
+
+Option<Error> validateImageID(const string& imageId)
+{
+  if (!strings::startsWith(imageId, "sha512-")) {
+    return Error("Image ID needs to start with sha512-");
+  }
+
+  string hash = strings::remove(imageId, "sha512-", strings::PREFIX);
+  if (hash.length() != 128) {
+    return Error("Invalid hash length for: " + hash);
+  }
+
+  return None();
+}
+
+
+Option<Error> validateLayout(const string& imagePath)
+{
+  if (!os::stat::isdir(paths::getImageRootfsPath(imagePath))) {
+    return Error("No rootfs directory found in image layout");
+  }
+
+  if (!os::stat::isfile(paths::getImageManifestPath(imagePath))) {
+    return Error("No manifest found in image layout");
+  }
+
+  return None();
+}
+
+
+Try<AppcImageManifest> parse(const string& value)
+{
+  Try<JSON::Object> json = JSON::parse<JSON::Object>(value);
+  if (json.isError()) {
+    return Error("JSON parse failed: " + json.error());
+  }
+
+  Try<AppcImageManifest> manifest =
+    protobuf::parse<AppcImageManifest>(json.get());
+
+  if (manifest.isError()) {
+    return Error("Protobuf parse failed: " + manifest.error());
+  }
+
+  Option<Error> error = validateManifest(manifest.get());
+  if (error.isSome()) {
+    return Error("Schema validation failed: " + error.get().message);
+  }
+
+  return manifest.get();
+}
+
+} // namespace spec {
+} // namespace appc {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/ab75d1d6/src/slave/containerizer/provisioners/appc/spec.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/appc/spec.hpp 
b/src/slave/containerizer/provisioners/appc/spec.hpp
new file mode 100644
index 0000000..5e4308a
--- /dev/null
+++ b/src/slave/containerizer/provisioners/appc/spec.hpp
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef __MESOS_APPC_SPEC__
+#define __MESOS_APPC_SPEC__
+
+#include <string>
+
+#include <stout/error.hpp>
+#include <stout/option.hpp>
+
+#include <mesos/mesos.hpp>
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace appc {
+namespace spec {
+
+// Validate if the specified image manifest conforms to the Appc spec.
+Option<Error> validateManifest(const AppcImageManifest& manifest);
+
+// Validate if the specified image ID conforms to the Appc spec.
+Option<Error> validateImageID(const std::string& imageId);
+
+// Validate if the specified image has the disk layout that conforms
+// to the Appc spec.
+Option<Error> validateLayout(const std::string& imagePath);
+
+// Parse the AppcImageManifest in the specified JSON string.
+Try<AppcImageManifest> parse(const std::string& value);
+
+} // namespace spec {
+} // namespace appc {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_APPC_SPEC__

http://git-wip-us.apache.org/repos/asf/mesos/blob/ab75d1d6/src/tests/containerizer/appc_provisioner_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/appc_provisioner_tests.cpp 
b/src/tests/containerizer/appc_provisioner_tests.cpp
new file mode 100644
index 0000000..1335cfd
--- /dev/null
+++ b/src/tests/containerizer/appc_provisioner_tests.cpp
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+
+#include <process/gtest.hpp>
+
+#include <stout/gtest.hpp>
+#include <stout/json.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+#include <stout/stringify.hpp>
+
+#include "slave/containerizer/provisioners/appc/spec.hpp"
+
+#include "tests/utils.hpp"
+
+using std::string;
+using std::vector;
+
+using namespace process;
+
+using namespace mesos::internal::slave::appc;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class AppcProvisionerTest : public TemporaryDirectoryTest {};
+
+
+TEST_F(AppcProvisionerTest, ValidateImageManifest)
+{
+  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();
+
+  EXPECT_SOME(spec::parse(stringify(manifest)));
+
+  // Incorrect acKind for image manifest.
+  manifest = JSON::parse(
+      "{"
+      "  \"acKind\": \"PodManifest\","
+      "  \"acVersion\": \"0.6.1\","
+      "  \"name\": \"foo.com/bar\""
+      "}").get();
+
+  EXPECT_ERROR(spec::parse(stringify(manifest)));
+}
+
+
+TEST_F(AppcProvisionerTest, ValidateLayout)
+{
+  string image = os::getcwd();
+
+  JSON::Value manifest = JSON::parse(
+      "{"
+      "  \"acKind\": \"ImageManifest\","
+      "  \"acVersion\": \"0.6.1\","
+      "  \"name\": \"foo.com/bar\""
+      "}").get();
+
+  ASSERT_SOME(os::write(path::join(image, "manifest"), stringify(manifest)));
+
+  // Missing rootfs.
+  EXPECT_SOME(spec::validateLayout(image));
+
+  ASSERT_SOME(os::mkdir(path::join(image, "rootfs", "tmp")));
+  ASSERT_SOME(os::write(path::join(image, "rootfs", "tmp", "test"), "test"));
+
+  EXPECT_NONE(spec::validateLayout(image));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

Reply via email to