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:28 UTC

[1/3] mesos git commit: Updated OCI protobuf messages with latest OCI image spec.

Repository: mesos
Updated Branches:
  refs/heads/master 52dfc2f00 -> 18f6642e2


Updated OCI protobuf messages with latest OCI image spec.

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


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

Branch: refs/heads/master
Commit: 87ca2fb0c9d179616a6c1b70882edac783bca34b
Parents: 52dfc2f
Author: Qian Zhang <zh...@gmail.com>
Authored: Tue Mar 28 16:50:03 2017 +0800
Committer: Qian Zhang <zh...@gmail.com>
Committed: Tue Mar 28 16:50:03 2017 +0800

----------------------------------------------------------------------
 include/mesos/oci/spec.proto | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/87ca2fb0/include/mesos/oci/spec.proto
----------------------------------------------------------------------
diff --git a/include/mesos/oci/spec.proto b/include/mesos/oci/spec.proto
index 97f3cf3..f7f1979 100644
--- a/include/mesos/oci/spec.proto
+++ b/include/mesos/oci/spec.proto
@@ -29,6 +29,11 @@ message Descriptor {
   required string digest = 2;
   required int64 size = 3;
   repeated string urls = 4;
+
+  // NOTE: We cannot use 'annotations' here because otherwise,
+  // json->protobuf parsing will fail. 'Annotations' is manually
+  // set during parsing.
+  repeated Label Annotations = 5;
 }
 
 
@@ -50,8 +55,13 @@ message ManifestDescriptor {
   required string mediaType = 1;
   required string digest = 2;
   required int64 size = 3;
-  required Platform platform = 4;
+  optional Platform platform = 4;
   repeated string urls = 5;
+
+  // NOTE: We cannot use 'annotations' here because otherwise,
+  // json->protobuf parsing will fail. 'Annotations' is manually
+  // set during parsing.
+  repeated Label Annotations = 6;
 }
 
 
@@ -65,13 +75,13 @@ message Label {
 
 
 /**
- * Protobuf for the OCI image manifest list JSON schema:
- * https://github.com/opencontainers/image-spec/blob/master/manifest-list.md
+ * Protobuf for the OCI image index JSON schema:
+ * https://github.com/opencontainers/image-spec/blob/master/image-index.md
  *
  * The OCI MIME type of this message is:
- *   application/vnd.oci.image.manifest.list.v1+json
+ *   application/vnd.oci.image.index.v1+json
  */
-message ManifestList {
+message Index {
   required int64 schemaVersion = 1;
   repeated ManifestDescriptor manifests = 2;
 


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

Posted by qi...@apache.org.
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();
 }
 


[3/3] mesos git commit: Update OCI tests with the latest OCI image spec.

Posted by qi...@apache.org.
Update OCI tests with the latest OCI image spec.

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


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

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

----------------------------------------------------------------------
 src/tests/containerizer/oci_spec_tests.cpp | 32 ++++++++++++-------------
 1 file changed, 16 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/18f6642e/src/tests/containerizer/oci_spec_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/oci_spec_tests.cpp b/src/tests/containerizer/oci_spec_tests.cpp
index 29a8f23..011e6d0 100644
--- a/src/tests/containerizer/oci_spec_tests.cpp
+++ b/src/tests/containerizer/oci_spec_tests.cpp
@@ -68,7 +68,7 @@ TEST_F(OCISpecTest, ParseDescriptor)
 }
 
 
-TEST_F(OCISpecTest, ParseManifestList)
+TEST_F(OCISpecTest, ParseIndex)
 {
   const string json =
       R"~(
@@ -104,54 +104,54 @@ TEST_F(OCISpecTest, ParseManifestList)
         }
       })~";
 
-  Try<image::v1::ManifestList> manifestList =
-      image::v1::parse<image::v1::ManifestList>(json);
+  Try<image::v1::Index> index =
+      image::v1::parse<image::v1::Index>(json);
 
-  ASSERT_SOME(manifestList);
+  ASSERT_SOME(index);
 
-  EXPECT_EQ(2u, manifestList->schemaversion());
+  EXPECT_EQ(2u, index->schemaversion());
 
   EXPECT_EQ(
       "application/vnd.oci.image.manifest.v1+json",
-      manifestList->manifests(0).mediatype());
+      index->manifests(0).mediatype());
 
-  EXPECT_EQ(7143u, manifestList->manifests(0).size());
+  EXPECT_EQ(7143u, index->manifests(0).size());
 
   EXPECT_EQ(
       "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
-      manifestList->manifests(0).digest());
+      index->manifests(0).digest());
 
   EXPECT_EQ(
       "ppc64le",
-      manifestList->manifests(0).platform().architecture());
+      index->manifests(0).platform().architecture());
 
   EXPECT_EQ(
       "linux",
-      manifestList->manifests(0).platform().os());
+      index->manifests(0).platform().os());
 
   EXPECT_EQ(
       "16.04",
-      manifestList->manifests(0).platform().os_version());
+      index->manifests(0).platform().os_version());
 
   EXPECT_EQ(
       "sse4",
-      manifestList->manifests(1).platform().os_features(0));
+      index->manifests(1).platform().os_features(0));
 
   EXPECT_EQ(
       "com.example.key1",
-      manifestList->annotations(0).key());
+      index->annotations(0).key());
 
   EXPECT_EQ(
       "value1",
-      manifestList->annotations(0).value());
+      index->annotations(0).value());
 
   EXPECT_EQ(
       "com.example.key2",
-      manifestList->annotations(1).key());
+      index->annotations(1).key());
 
   EXPECT_EQ(
       "value2",
-      manifestList->annotations(1).value());
+      index->annotations(1).value());
 }