You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by qi...@apache.org on 2017/03/28 09:24:29 UTC

[2/3] mesos git commit: Updated OCI spec parsing & validation code with latest OCI image spec.

Updated OCI spec parsing & validation code with latest OCI image spec.

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


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

Branch: refs/heads/master
Commit: 0c1d50d236dbc100b9cbab194daba409483eed7a
Parents: 87ca2fb
Author: Qian Zhang <zh...@gmail.com>
Authored: Tue Mar 28 16:50:11 2017 +0800
Committer: Qian Zhang <zh...@gmail.com>
Committed: Tue Mar 28 16:50:11 2017 +0800

----------------------------------------------------------------------
 include/mesos/oci/spec.hpp |  15 +++-
 src/oci/spec.cpp           | 152 +++++++++++++++++++++++++++-------------
 2 files changed, 116 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/0c1d50d2/include/mesos/oci/spec.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/oci/spec.hpp b/include/mesos/oci/spec.hpp
index ea4f29e..d8eef84 100644
--- a/include/mesos/oci/spec.hpp
+++ b/include/mesos/oci/spec.hpp
@@ -26,8 +26,8 @@ namespace v1 {
 
 // Constant strings for OCI image media types:
 // https://github.com/opencontainers/image-spec/blob/master/media-types.md
-constexpr char MEDIA_TYPE_MANIFEST_LIST[] =
-    "application/vnd.oci.image.manifest.list.v1+json";
+constexpr char MEDIA_TYPE_INDEX[] =
+    "application/vnd.oci.image.index.v1+json";
 
 constexpr char MEDIA_TYPE_MANIFEST[] =
     "application/vnd.oci.image.manifest.v1+json";
@@ -36,13 +36,22 @@ constexpr char MEDIA_TYPE_CONFIG[] =
     "application/vnd.oci.image.config.v1+json";
 
 constexpr char MEDIA_TYPE_LAYER[] =
+    "application/vnd.oci.image.layer.v1.tar";
+
+constexpr char MEDIA_TYPE_LAYER_GZIP[] =
     "application/vnd.oci.image.layer.v1.tar+gzip";
 
 constexpr char MEDIA_TYPE_NONDIST_LAYER[] =
+    "application/vnd.oci.image.layer.nondistributable.v1.tar";
+
+constexpr char MEDIA_TYPE_NONDIST_LAYER_GZIP[] =
     "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip";
 
+// Rootfs type of OCI image configuration.
+constexpr char ROOTFS_TYPE[] = "layers";
+
 /**
- * Returns the OCI v1 descriptor, image manifest list, image manifest
+ * Returns the OCI v1 descriptor, image index, image manifest
  * and image configuration from the given string.
  */
 template <typename T>

http://git-wip-us.apache.org/repos/asf/mesos/blob/0c1d50d2/src/oci/spec.cpp
----------------------------------------------------------------------
diff --git a/src/oci/spec.cpp b/src/oci/spec.cpp
index 0a88edb..06fceb4 100644
--- a/src/oci/spec.cpp
+++ b/src/oci/spec.cpp
@@ -43,25 +43,20 @@ Option<Error> validateDigest(const string& digest)
 }
 
 
-Option<Error> validate(const ManifestList& manifestList)
+Option<Error> validate(const Index& index)
 {
-  if (manifestList.schemaversion() != 2) {
+  if (index.schemaversion() != 2) {
     return Error(
         "Incorrect 'schemaVersion': " +
-        stringify(manifestList.schemaversion()));
+        stringify(index.schemaversion()));
   }
 
-  foreach (const ManifestDescriptor& manifest, manifestList.manifests()) {
+  foreach (const ManifestDescriptor& manifest, index.manifests()) {
     Option<Error> error = validateDigest(manifest.digest());
     if (error.isSome()) {
       return Error(
           "Failed to validate 'digest' of the 'manifest': " + error->message);
     }
-
-    if (manifest.mediatype() != MEDIA_TYPE_MANIFEST) {
-      return Error(
-          "Incorrect 'mediaType' of the 'manifest': " + manifest.mediatype());
-    }
   }
 
   return None();
@@ -101,7 +96,9 @@ Option<Error> validate(const Manifest& manifest)
     }
 
     if (layer.mediatype() != MEDIA_TYPE_LAYER &&
-        layer.mediatype() != MEDIA_TYPE_NONDIST_LAYER) {
+        layer.mediatype() != MEDIA_TYPE_LAYER_GZIP &&
+        layer.mediatype() != MEDIA_TYPE_NONDIST_LAYER &&
+        layer.mediatype() != MEDIA_TYPE_NONDIST_LAYER_GZIP) {
       return Error(
           "Incorrect 'mediaType' of the 'layer': " + layer.mediatype());
     }
@@ -110,6 +107,16 @@ Option<Error> validate(const Manifest& manifest)
   return None();
 }
 
+
+Option<Error> validate(const Configuration& configuration)
+{
+  if (configuration.rootfs().type() != ROOTFS_TYPE) {
+    return Error("Incorrect 'type': " + configuration.rootfs().type());
+  }
+
+  return None();
+}
+
 } // namespace internal {
 
 
@@ -126,6 +133,28 @@ Try<Descriptor> parse(const string& s)
     return Error("Protobuf parse failed: " + descriptor.error());
   }
 
+  // Manually parse 'annotations' field.
+  Result<JSON::Value> annotations = json->find<JSON::Value>("annotations");
+  if (annotations.isError()) {
+    return Error(
+        "Failed to find 'annotations': " + annotations.error());
+  }
+
+  if (annotations.isSome() && !annotations->is<JSON::Null>()) {
+    foreachpair (const string& key,
+                 const JSON::Value& value,
+                 annotations->as<JSON::Object>().values) {
+      if (!value.is<JSON::String>()) {
+        return Error(
+            "The value of annotation key '" + key + "' is not a JSON string");
+      }
+
+      Label* annotation = descriptor->add_annotations();
+      annotation->set_key(key);
+      annotation->set_value(value.as<JSON::String>().value);
+    }
+  }
+
   Option<Error> error = internal::validateDigest(descriptor->digest());
   if (error.isSome()) {
     return Error(
@@ -137,20 +166,20 @@ Try<Descriptor> parse(const string& s)
 
 
 template <>
-Try<ManifestList> parse(const string& s)
+Try<Index> parse(const string& s)
 {
   Try<JSON::Object> json = JSON::parse<JSON::Object>(s);
   if (json.isError()) {
     return Error("JSON parse failed: " + json.error());
   }
 
-  Try<ManifestList> manifestList = protobuf::parse<ManifestList>(json.get());
-  if (manifestList.isError()) {
-    return Error("Protobuf parse failed: " + manifestList.error());
+  Try<Index> index = protobuf::parse<Index>(json.get());
+  if (index.isError()) {
+    return Error("Protobuf parse failed: " + index.error());
   }
 
-  // Manually parse 'manifest.platform.os.version' and
-  // 'manifest.platform.os.features'.
+  // Manually parse 'manifest.annotations', 'manifest.platform.os.version'
+  // and 'manifest.platform.os.features'.
   Result<JSON::Array> manifests = json->at<JSON::Array>("manifests");
   if (manifests.isError()) {
     return Error("Failed to find 'manifests': " + manifests.error());
@@ -172,9 +201,9 @@ Try<ManifestList> parse(const string& s)
     }
 
     ManifestDescriptor* _manifest = nullptr;
-    for (int i = 0; i < manifestList->manifests_size(); i++) {
-      if (manifestList->manifests(i).digest() == digest.get()) {
-        _manifest = manifestList->mutable_manifests(i);
+    for (int i = 0; i < index->manifests_size(); i++) {
+      if (index->manifests(i).digest() == digest.get()) {
+        _manifest = index->mutable_manifests(i);
         break;
       }
     }
@@ -185,40 +214,61 @@ Try<ManifestList> parse(const string& s)
           digest->value + "'");
     }
 
-    Result<JSON::Object> platform = manifest.at<JSON::Object>("platform");
-    if (platform.isError()) {
-      return Error("Failed to find 'platform': " + platform.error());
-    } else if (platform.isNone()) {
-      return Error("Unable to find 'platform'");
-    }
-
-    Result<JSON::String> osVersion = platform->at<JSON::String>("os.version");
-    if (osVersion.isError()) {
+    Result<JSON::Value> annotations = manifest.find<JSON::Value>("annotations");
+    if (annotations.isError()) {
       return Error(
-          "Failed to find 'platform.os.version': " + osVersion.error());
+          "Failed to find 'annotations': " + annotations.error());
     }
 
-    if (osVersion.isSome()) {
-      Platform* platform = _manifest->mutable_platform();
-      platform->set_os_version(osVersion->value);
+    if (annotations.isSome() && !annotations->is<JSON::Null>()) {
+      foreachpair (const string& key,
+                   const JSON::Value& value,
+                   annotations->as<JSON::Object>().values) {
+        if (!value.is<JSON::String>()) {
+          return Error(
+              "The value of annotation key '" + key + "' is not a JSON string");
+        }
+
+        Label* annotation = _manifest->add_annotations();
+        annotation->set_key(key);
+        annotation->set_value(value.as<JSON::String>().value);
+      }
     }
 
-    Result<JSON::Array> osFeatures = platform->at<JSON::Array>("os.features");
-    if (osFeatures.isError()) {
-      return Error(
-          "Failed to find 'platform.os.features': " + osFeatures.error());
+    Result<JSON::Object> platform = manifest.at<JSON::Object>("platform");
+    if (platform.isError()) {
+      return Error("Failed to find 'platform': " + platform.error());
     }
 
-    if (osFeatures.isSome()) {
-      const vector<JSON::Value>& values = osFeatures->values;
-      if (values.size() != 0) {
+    if (platform.isSome()) {
+      Result<JSON::String> osVersion = platform->at<JSON::String>("os.version");
+      if (osVersion.isError()) {
+        return Error(
+            "Failed to find 'platform.os.version': " + osVersion.error());
+      }
+
+      if (osVersion.isSome()) {
         Platform* platform = _manifest->mutable_platform();
-        foreach (const JSON::Value& value, values) {
-          if (!value.is<JSON::String>()) {
-            return Error("Expecting OS feature to be string type");
-          }
+        platform->set_os_version(osVersion->value);
+      }
 
-          platform->add_os_features(value.as<JSON::String>().value);
+      Result<JSON::Array> osFeatures = platform->at<JSON::Array>("os.features");
+      if (osFeatures.isError()) {
+        return Error(
+            "Failed to find 'platform.os.features': " + osFeatures.error());
+      }
+
+      if (osFeatures.isSome()) {
+        const vector<JSON::Value>& values = osFeatures->values;
+        if (values.size() != 0) {
+          Platform* platform = _manifest->mutable_platform();
+          foreach (const JSON::Value& value, values) {
+            if (!value.is<JSON::String>()) {
+              return Error("Expecting OS feature to be string type");
+            }
+
+            platform->add_os_features(value.as<JSON::String>().value);
+          }
         }
       }
     }
@@ -240,19 +290,19 @@ Try<ManifestList> parse(const string& s)
             "The value of annotation key '" + key + "' is not a JSON string");
       }
 
-      Label* annotation = manifestList->add_annotations();
+      Label* annotation = index->add_annotations();
       annotation->set_key(key);
       annotation->set_value(value.as<JSON::String>().value);
     }
   }
 
-  Option<Error> error = internal::validate(manifestList.get());
+  Option<Error> error = internal::validate(index.get());
   if (error.isSome()) {
     return Error(
-        "OCI v1 image manifest list validation failed: " + error->message);
+        "OCI v1 image index validation failed: " + error->message);
   }
 
-  return manifestList.get();
+  return index.get();
 }
 
 
@@ -368,6 +418,12 @@ Try<Configuration> parse(const string& s)
     }
   }
 
+  Option<Error> error = internal::validate(configuration.get());
+  if (error.isSome()) {
+    return Error(
+        "OCI v1 image configuration validation failed: " + error->message);
+  }
+
   return configuration.get();
 }