blob: 319da4247cbca00b2e0f7534462a42c7f96ad04c [file] [log] [blame]
// 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 <gtest/gtest.h>
#include <set>
#include <string>
#include <vector>
#include <mesos/mesos.hpp>
#include <mesos/type_utils.hpp>
#include <stout/stringify.hpp>
#include "common/protobuf_utils.hpp"
#include "tests/mesos.hpp"
using std::set;
using std::string;
using std::vector;
using google::protobuf::Map;
using mesos::internal::protobuf::convertLabelsToStringMap;
using mesos::internal::protobuf::convertStringMapToLabels;
using mesos::internal::protobuf::createLabel;
namespace mesos {
namespace internal {
namespace tests {
// This tests that helper function `getRoles` can correctly
// get roles from multi-role FrameworkInfo and role from
// single-role FrameworkInfo.
TEST(ProtobufUtilTest, GetRoles)
{
// Get roles from a multi-role framework.
{
FrameworkInfo frameworkInfo;
frameworkInfo.add_capabilities()->set_type(
FrameworkInfo::Capability::MULTI_ROLE);
frameworkInfo.add_roles("bar");
frameworkInfo.add_roles("qux");
set<string> roles = protobuf::framework::getRoles(frameworkInfo);
EXPECT_EQ(roles, set<string>({"qux", "bar"}));
}
// Get role from a single-role framework.
{
FrameworkInfo frameworkInfo;
frameworkInfo.set_role("foo");
set<string> roles = protobuf::framework::getRoles(frameworkInfo);
EXPECT_EQ(roles, set<string>({"foo"}));
}
}
// Tests that allocation info can be injected to and stripped from
// offer operations.
TEST(ProtobufUtilTest, InjectAndStripAllocationInfoInOfferOperation)
{
Resources unallocatedResources = Resources::parse("cpus:1").get();
Resources allocatedResources = unallocatedResources;
allocatedResources.allocate("role");
Resource::AllocationInfo allocationInfo;
allocationInfo.set_role("role");
ExecutorInfo executorInfo;
executorInfo.mutable_resources()->CopyFrom(unallocatedResources);
TaskInfo taskInfo;
taskInfo.mutable_resources()->CopyFrom(unallocatedResources);
taskInfo.mutable_executor()->CopyFrom(executorInfo);
{
// Test the LAUNCH case. This should be constructing a valid
// task and executor, but for now this just sets the resources
// in order to verify the allocation info injection and stripping.
Offer::Operation launch = LAUNCH({taskInfo});
protobuf::injectAllocationInfo(&launch, allocationInfo);
ASSERT_EQ(1, launch.launch().task_infos_size());
EXPECT_EQ(allocatedResources,
launch.launch().task_infos(0).resources());
EXPECT_EQ(allocatedResources,
launch.launch().task_infos(0).executor().resources());
protobuf::stripAllocationInfo(&launch);
EXPECT_EQ(unallocatedResources,
launch.launch().task_infos(0).resources());
EXPECT_EQ(unallocatedResources,
launch.launch().task_infos(0).executor().resources());
}
{
// Test the LAUNCH_GROUP case. This should be constructing a valid
// task and executor, but for now this just sets the resources in
// order to verify the allocation info injection.
TaskGroupInfo taskGroupInfo;
taskGroupInfo.add_tasks()->CopyFrom(taskInfo);
Offer::Operation launchGroup = LAUNCH_GROUP(executorInfo, taskGroupInfo);
protobuf::injectAllocationInfo(&launchGroup, allocationInfo);
ASSERT_EQ(1, launchGroup.launch_group().task_group().tasks_size());
EXPECT_EQ(allocatedResources,
launchGroup.launch_group().task_group().tasks(0).resources());
EXPECT_EQ(allocatedResources,
launchGroup.launch_group().task_group().tasks(0).executor()
.resources());
protobuf::stripAllocationInfo(&launchGroup);
EXPECT_EQ(unallocatedResources,
launchGroup.launch_group().task_group().tasks(0).resources());
EXPECT_EQ(unallocatedResources,
launchGroup.launch_group().task_group().tasks(0).executor()
.resources());
}
{
// Test the RESERVE case. This should be constructing a valid
// reservation, but for now this just sets the resources in
// order to verify the allocation info injection.
Offer::Operation reserve = RESERVE(unallocatedResources);
protobuf::injectAllocationInfo(&reserve, allocationInfo);
EXPECT_EQ(allocatedResources, reserve.reserve().resources());
protobuf::stripAllocationInfo(&reserve);
EXPECT_EQ(unallocatedResources, reserve.reserve().resources());
}
{
// Test the UNRESERVE case. This should be constructing a valid
// reservation, but for now this just sets the resources in
// order to verify the allocation info injection.
Offer::Operation unreserve = UNRESERVE(unallocatedResources);
protobuf::injectAllocationInfo(&unreserve, allocationInfo);
EXPECT_EQ(allocatedResources, unreserve.unreserve().resources());
protobuf::stripAllocationInfo(&unreserve);
EXPECT_EQ(unallocatedResources, unreserve.unreserve().resources());
}
{
// Test the CREATE case. This should be constructing a valid
// volume, but for now this just sets the resources in order
// to verify the allocation info injection.
Offer::Operation create = CREATE(unallocatedResources);
protobuf::injectAllocationInfo(&create, allocationInfo);
EXPECT_EQ(allocatedResources, create.create().volumes());
protobuf::stripAllocationInfo(&create);
EXPECT_EQ(unallocatedResources, create.create().volumes());
}
{
// Test the DESTROY case. This should be constructing a valid
// volume, but for now this just sets the resources in order
// to verify the allocation info injection.
Offer::Operation destroy = DESTROY(unallocatedResources);
protobuf::injectAllocationInfo(&destroy, allocationInfo);
EXPECT_EQ(allocatedResources, destroy.destroy().volumes());
protobuf::stripAllocationInfo(&destroy);
EXPECT_EQ(unallocatedResources, destroy.destroy().volumes());
}
}
// This tests that helper function `convertLabelsToStringMap` can
// correctly convert a `Labels` to a protobuf string map and helper
// function `convertStringMapToLabels` can convert it back.
TEST(ProtobufUtilTest, ConvertBetweenLabelsAndStringMap)
{
Labels labels1;
labels1.add_labels()->CopyFrom(createLabel("foo", "bar"));
Try<Map<string, string>> map1 = convertLabelsToStringMap(labels1);
ASSERT_SOME(map1);
ASSERT_NE(map1->end(), map1->find("foo"));
EXPECT_EQ("bar", map1->at("foo"));
EXPECT_EQ(labels1, convertStringMapToLabels(map1.get()));
Labels labels2;
labels2.add_labels()->CopyFrom(createLabel("foo", "bar"));
labels2.add_labels()->CopyFrom(createLabel("foo", "baz"));
EXPECT_ERROR(convertLabelsToStringMap(labels2));
Labels labels3;
labels3.add_labels()->CopyFrom(createLabel("foo", None()));
EXPECT_ERROR(convertLabelsToStringMap(labels3));
}
// This tests that Capabilities are correctly constructed
// from given FrameworkInfo Capabilities.
TEST(ProtobufUtilTest, FrameworkCapabilities)
{
auto toTypeSet = [](
const protobuf::framework::Capabilities& capabilities) {
set<FrameworkInfo::Capability::Type> result;
if (capabilities.revocableResources) {
result.insert(FrameworkInfo::Capability::REVOCABLE_RESOURCES);
}
if (capabilities.taskKillingState) {
result.insert(FrameworkInfo::Capability::TASK_KILLING_STATE);
}
if (capabilities.gpuResources) {
result.insert(FrameworkInfo::Capability::GPU_RESOURCES);
}
if (capabilities.sharedResources) {
result.insert(FrameworkInfo::Capability::SHARED_RESOURCES);
}
if (capabilities.partitionAware) {
result.insert(FrameworkInfo::Capability::PARTITION_AWARE);
}
if (capabilities.multiRole) {
result.insert(FrameworkInfo::Capability::MULTI_ROLE);
}
if (capabilities.regionAware) {
result.insert(FrameworkInfo::Capability::REGION_AWARE);
}
return result;
};
auto typeSetToCapabilityVector = [](
const set<FrameworkInfo::Capability::Type>& capabilitiesTypes) {
vector<FrameworkInfo::Capability> result;
foreach (FrameworkInfo::Capability::Type type, capabilitiesTypes) {
FrameworkInfo::Capability capability;
capability.set_type(type);
result.push_back(capability);
}
return result;
};
// We test the `Capabilities` construction by converting back
// to types and checking for equality with the original types.
auto backAndForth = [=](const set<FrameworkInfo::Capability::Type>& types) {
protobuf::framework::Capabilities capabilities(
typeSetToCapabilityVector(types));
return toTypeSet(capabilities);
};
set<FrameworkInfo::Capability::Type> expected;
expected = { FrameworkInfo::Capability::REVOCABLE_RESOURCES };
EXPECT_EQ(expected, backAndForth(expected));
expected = { FrameworkInfo::Capability::TASK_KILLING_STATE };
EXPECT_EQ(expected, backAndForth(expected));
expected = { FrameworkInfo::Capability::GPU_RESOURCES };
EXPECT_EQ(expected, backAndForth(expected));
expected = { FrameworkInfo::Capability::SHARED_RESOURCES };
EXPECT_EQ(expected, backAndForth(expected));
expected = { FrameworkInfo::Capability::PARTITION_AWARE };
EXPECT_EQ(expected, backAndForth(expected));
expected = { FrameworkInfo::Capability::MULTI_ROLE };
EXPECT_EQ(expected, backAndForth(expected));
expected = { FrameworkInfo::Capability::REGION_AWARE };
EXPECT_EQ(expected, backAndForth(expected));
}
// This tests that Capabilities are correctly constructed
// from given Agent Capabilities.
TEST(ProtobufUtilTest, AgentCapabilities)
{
// TODO(jay_guo): consider applying the same test style in
// FrameworkCapabilities when we have more capabilities in agent.
RegisterSlaveMessage registerSlaveMessage;
registerSlaveMessage.add_agent_capabilities()->set_type(
SlaveInfo::Capability::MULTI_ROLE);
protobuf::slave::Capabilities capabilities(
registerSlaveMessage.agent_capabilities());
ASSERT_TRUE(capabilities.multiRole);
}
// Test large message evolve.
// Before protobuf 3.3.0, this test would fail due to the 64MB limit
// imposed by older version of protobuf.
TEST(ProtobufUtilTest, LargeMessageEvolve)
{
string data =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
"ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
"aliquip ex ea commodo consequat. Duis aute irure dolor in "
"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
"culpa qui officia deserunt mollit anim id est laborum.";
while (Bytes(data.size()) < Megabytes(70)) {
data.append(data);
}
ExecutorInfo executorInfo_;
executorInfo_.set_data(data);
evolve(executorInfo_);
}
TEST(ProtobufUtilTest, ParseContainerID)
{
ContainerID parent;
parent.set_value("parent");
ContainerID child;
child.set_value("child");
child.mutable_parent()->CopyFrom(parent);
EXPECT_EQ(parent, protobuf::parseContainerId(stringify(parent)));
EXPECT_EQ(child, protobuf::parseContainerId(stringify(child)));
}
} // namespace tests {
} // namespace internal {
} // namespace mesos {