@@ -132,6 +132,7 @@ static train_result<Task> call_daal_kernel(const context_cpu& ctx,
         daal::services::internal::hostApp(daal_input),
         daal_data.get(),
         daal_labels.get(),
+        nullptr, // no weights
         *mptr,
         daal_result,
         daal_parameter));

/*******************************************************************************
* Copyright 2020 Intel Corporation
*
* Licensed 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 <daal/include/services/error_handling.h>
#include <daal/src/algorithms/dtrees/forest/classification/df_classification_model_impl.h>
#include <daal/src/services/service_algo_utils.h>

#include <daal/include/algorithms/decision_forest/decision_forest_classification_training_batch.h>
#include <daal/include/algorithms/decision_forest/decision_forest_classification_training_types.h>

#include <daal/src/algorithms/dtrees/forest/classification/df_classification_train_kernel.h>
//to prevent reordering by clang-format
#include <daal/src/algorithms/dtrees/forest/classification/df_classification_train_dense_default_kernel.h>

#include "oneapi/dal/algo/decision_forest/backend/cpu/train_kernel.hpp"
#include "oneapi/dal/algo/decision_forest/backend/interop_helpers.hpp"
#include "oneapi/dal/backend/interop/common.hpp"
#include "oneapi/dal/backend/interop/error_converter.hpp"
#include "oneapi/dal/backend/interop/table_conversion.hpp"
#include "oneapi/dal/detail/common.hpp"

#include "oneapi/dal/table/row_accessor.hpp"

namespace oneapi::dal::decision_forest::backend {

using dal::backend::context_cpu;

namespace df = daal::algorithms::decision_forest;
namespace cls = daal::algorithms::decision_forest::classification;

namespace interop = dal::backend::interop;
namespace df_interop = dal::backend::interop::decision_forest;

template <typename Float, daal::CpuType Cpu>
using cls_dense_kernel_t = cls::training::internal::
    ClassificationTrainBatchKernel<Float, cls::training::defaultDense, Cpu>;

using cls_model_p = cls::ModelPtr;

template <typename Float, typename Task>
static train_result<Task> call_daal_kernel(const context_cpu& ctx,
                                           const descriptor_base<Task>& desc,
                                           const table& data,
                                           const table& labels) {
    const int64_t row_count = data.get_row_count();
    const int64_t column_count = data.get_column_count();

    auto arr_data = row_accessor<const Float>{ data }.pull();
    auto arr_label = row_accessor<const Float>{ labels }.pull();

    const auto daal_data =
        interop::convert_to_daal_homogen_table(arr_data, row_count, column_count);
    const auto daal_labels = interop::convert_to_daal_homogen_table(arr_label, row_count, 1);

    /* init param for daal kernel */
    auto daal_input = daal::algorithms::classifier::training::Input();
    daal_input.set(daal::algorithms::classifier::training::data, daal_data);
    daal_input.set(daal::algorithms::classifier::training::labels, daal_labels);

    auto daal_parameter = cls::training::Parameter(desc.get_class_count());
    daal_parameter.nTrees = desc.get_tree_count();
    daal_parameter.observationsPerTreeFraction = desc.get_observations_per_tree_fraction();
    daal_parameter.featuresPerNode = desc.get_features_per_node();
    daal_parameter.maxTreeDepth = desc.get_max_tree_depth();
    daal_parameter.minObservationsInLeafNode = desc.get_min_observations_in_leaf_node();
    // TODO take engines from desc
    daal_parameter.engine = daal::algorithms::engines::mt2203::Batch<>::create();
    daal_parameter.impurityThreshold = desc.get_impurity_threshold();
    daal_parameter.memorySavingMode = desc.get_memory_saving_mode();
    daal_parameter.bootstrap = desc.get_bootstrap();
    daal_parameter.minObservationsInSplitNode = desc.get_min_observations_in_split_node();
    daal_parameter.minWeightFractionInLeafNode = desc.get_min_weight_fraction_in_leaf_node();
    daal_parameter.minImpurityDecreaseInSplitNode = desc.get_min_impurity_decrease_in_split_node();
    daal_parameter.maxLeafNodes = desc.get_max_leaf_nodes();

    daal_parameter.resultsToCompute = static_cast<std::uint64_t>(desc.get_error_metric_mode());

    auto vimp = desc.get_variable_importance_mode();

    daal_parameter.varImportance = df_interop::convert_to_daal_variable_importance_mode(vimp);

    train_result<Task> res;

    auto daal_result = cls::training::Result();

    /* init daal result's objects */
    if (check_mask_flag(desc.get_error_metric_mode(), error_metric_mode::out_of_bag_error)) {
        auto arr_oob_err = array<Float>::empty(1 * 1);
        res.set_oob_err(dal::detail::homogen_table_builder{}.reset(arr_oob_err, 1, 1).build());

        const auto res_oob_err = interop::convert_to_daal_homogen_table(arr_oob_err, 1, 1);
        daal_result.set(cls::training::outOfBagError, res_oob_err);
    }

    if (check_mask_flag(desc.get_error_metric_mode(),
                        error_metric_mode::out_of_bag_error_per_observation)) {
        auto arr_oob_per_obs_err = array<Float>::empty(row_count * 1);
        res.set_oob_err_per_observation(
            dal::detail::homogen_table_builder{}.reset(arr_oob_per_obs_err, row_count, 1).build());

        const auto res_oob_per_obs_err =
            interop::convert_to_daal_homogen_table(arr_oob_per_obs_err, row_count, 1);
        daal_result.set(cls::training::outOfBagErrorPerObservation, res_oob_per_obs_err);
    }
    if (variable_importance_mode::none != vimp) {
        auto arr_var_imp = array<Float>::empty(1 * column_count);
        res.set_var_importance(
            dal::detail::homogen_table_builder{}.reset(arr_var_imp, 1, column_count).build());

        const auto res_var_imp =
            interop::convert_to_daal_homogen_table(arr_var_imp, 1, column_count);
        daal_result.set(cls::training::variableImportance, res_var_imp);
    }

    cls::ModelPtr mptr = cls::ModelPtr(new cls::internal::ModelImpl(column_count));

    interop::status_to_exception(interop::call_daal_kernel<Float, cls_dense_kernel_t>(
        ctx,
        daal::services::internal::hostApp(daal_input),
        daal_data.get(),
        daal_labels.get(),
        *mptr,
        daal_result,
        daal_parameter));

    /* extract results from daal objects */
    if (check_mask_flag(desc.get_error_metric_mode(), error_metric_mode::out_of_bag_error)) {
        auto table_oob_err = interop::convert_from_daal_homogen_table<Float>(
            daal_result.get(cls::training::outOfBagError));
        res.set_oob_err(table_oob_err);
    }

    if (check_mask_flag(desc.get_error_metric_mode(),
                        error_metric_mode::out_of_bag_error_per_observation)) {
        auto table_oob_per_obs_err = interop::convert_from_daal_homogen_table<Float>(
            daal_result.get(cls::training::outOfBagErrorPerObservation));
        res.set_oob_err_per_observation(table_oob_per_obs_err);
    }

    if (variable_importance_mode::none != vimp) {
        auto table_var_imp = interop::convert_from_daal_homogen_table<Float>(
            daal_result.get(cls::training::variableImportance));
        res.set_var_importance(table_var_imp);
    }

    return res.set_model(dal::detail::pimpl_accessor().make_from_pimpl<model<Task>>(
        std::make_shared<interop::decision_forest::interop_model_impl<Task, cls_model_p>>(mptr)));
}

template <typename Float, typename Task>
static train_result<Task> train(const context_cpu& ctx,
                                const descriptor_base<Task>& desc,
                                const train_input<Task>& input) {
    return call_daal_kernel<Float>(ctx, desc, input.get_data(), input.get_labels());
}

template <typename Float, typename Task>
struct train_kernel_cpu<Float, Task, method::dense> {
    train_result<Task> operator()(const context_cpu& ctx,
                                  const descriptor_base<Task>& desc,
                                  const train_input<Task>& input) const {
        return train<Float, Task>(ctx, desc, input);
    }
};

template struct train_kernel_cpu<float, task::classification, method::dense>;
template struct train_kernel_cpu<double, task::classification, method::dense>;

} // namespace oneapi::dal::decision_forest::backend

Please add full weights setting/getting support for train input, thus it can be used here instead of nullptr


@@ -55,10 +55,43 @@ static train_result call_daal_kernel(const context_gpu& ctx,
     auto arr_data  = row_accessor<const Float>{ data }.pull(queue);
     auto arr_label = row_accessor<const Float>{ labels }.pull(queue);

+    // TODO: make for dpcpp kernel
+    auto arr_new_label = array<Float>::empty(queue, row_count * 1);
+    arr_label.need_mutable_data(queue);
+    Float value_first_class_label  = arr_label[0];
+    Float value_second_class_label = arr_label[1];
+
+    arr_new_label[0] = Float(-1.0);
+    std::int64_t i   = 1;
+    for (; i < row_count; ++i) {
+        if (arr_label[i] == value_first_class_label) {
+            arr_new_label[i] = Float(-1.0);
+        }
+        else {
+            value_second_class_label = arr_label[i];
+            arr_new_label[i]         = Float(+1.0);
+        }
+    }
+    if (value_first_class_label == value_second_class_label) {
+        throw invalid_argument("Input label data should have more one unique label");
+    }
+
+    for (; i < row_count; ++i) {
+        if (arr_label[i] == value_first_class_label) {
+            arr_new_label[i] = Float(-1.0);
+        }
+        else if (arr_label[i] == value_second_class_label) {
+            arr_new_label[i] = Float(+1.0);
+        }
+        else {
+            throw invalid_argument("Input label data should have only two unique labels");
+        }
+    }
+
     const auto daal_data =
         interop::convert_to_daal_sycl_homogen_table(queue, arr_data, row_count, column_count);
     const auto daal_labels =
-        interop::convert_to_daal_sycl_homogen_table(queue, arr_label, row_count, 1);
+        interop::convert_to_daal_sycl_homogen_table(queue, arr_new_label, row_count, 1);

     auto kernel_impl       = desc.get_kernel_impl()->get_impl();
     const auto daal_kernel = kernel_impl->get_daal_kernel_function();

/*******************************************************************************
* Copyright 2020 Intel Corporation
*
* Licensed 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.
*******************************************************************************/

#define DAAL_SYCL_INTERFACE
#define DAAL_SYCL_INTERFACE_USM
#define DAAL_SYCL_INTERFACE_REVERSED_RANGE

#include "oneapi/dal/algo/svm/backend/gpu/train_kernel.hpp"
#include "oneapi/dal/algo/svm/backend/interop_model.hpp"
#include "oneapi/dal/algo/svm/backend/kernel_function_impl.hpp"
#include "oneapi/dal/backend/interop/common_dpc.hpp"
#include "oneapi/dal/backend/interop/error_converter.hpp"
#include "oneapi/dal/backend/interop/table_conversion.hpp"

#include <daal/src/algorithms/svm/oneapi/svm_train_thunder_kernel_oneapi.h>

namespace oneapi::dal::svm::backend {

using std::int64_t;
using dal::backend::context_gpu;

namespace daal_svm             = daal::algorithms::svm;
namespace daal_kernel_function = daal::algorithms::kernel_function;
namespace interop              = dal::backend::interop;

template <typename Float>
using daal_svm_thunder_kernel_t = daal_svm::training::internal::
    SVMTrainOneAPI<Float, daal_svm::Parameter, daal_svm::training::thunder>;

template <typename Float>
static train_result call_daal_kernel(const context_gpu& ctx,
                                     const descriptor_base& desc,
                                     const table& data,
                                     const table& labels) {
    auto& queue = ctx.get_queue();
    interop::execution_context_guard guard(queue);

    const int64_t row_count    = data.get_row_count();
    const int64_t column_count = data.get_column_count();

    // TODO: data is table, not a homogen_table. Think better about accessor - is it enough to have just a row_accessor?
    auto arr_data  = row_accessor<const Float>{ data }.pull(queue);
    auto arr_label = row_accessor<const Float>{ labels }.pull(queue);

    const auto daal_data =
        interop::convert_to_daal_sycl_homogen_table(queue, arr_data, row_count, column_count);
    const auto daal_labels =
        interop::convert_to_daal_sycl_homogen_table(queue, arr_label, row_count, 1);

    auto kernel_impl       = desc.get_kernel_impl()->get_impl();
    const auto daal_kernel = kernel_impl->get_daal_kernel_function();
    daal_svm::Parameter daal_parameter(
        daal_kernel,
        desc.get_c(),
        desc.get_accuracy_threshold(),
        desc.get_tau(),
        desc.get_max_iteration_count(),
        int64_t(desc.get_cache_size() * 1024 * 1024), // DAAL get in bytes
        desc.get_shrinking());

    auto daal_model = daal_svm::Model::create<Float>(column_count);
    interop::status_to_exception(daal_svm_thunder_kernel_t<Float>().compute(daal_data,
                                                                            *daal_labels,
                                                                            daal_model.get(),
                                                                            &daal_parameter));
    auto table_support_indices =
        interop::convert_from_daal_homogen_table<Float>(daal_model->getSupportIndices());

    return train_result()
        .set_model(convert_from_daal_model<Float>(*daal_model))
        .set_support_indices(table_support_indices);
}

template <typename Float>
static train_result train(const context_gpu& ctx,
                          const descriptor_base& desc,
                          const train_input& input) {
    return call_daal_kernel<Float>(ctx, desc, input.get_data(), input.get_labels());
}

template <typename Float>
struct train_kernel_gpu<Float, task::classification, method::thunder> {
    train_result operator()(const dal::backend::context_gpu& ctx,
                            const descriptor_base& desc,
                            const train_input& input) const {
        return train<Float>(ctx, desc, input);
    }
};

template struct train_kernel_gpu<float, task::classification, method::thunder>;
template struct train_kernel_gpu<double, task::classification, method::thunder>;

} // namespace oneapi::dal::svm::backend

You can use the method extracted earlier in CPU backend rather than copy-paste


@@ -42,6 +42,22 @@ DEFINE_int32(rocksdb_batch_size,
 DEFINE_string(part_man_type,
               "memory",
               "memory, meta");
+
+static bool validateRocksdbStatsLevel(const char* flagname, int value) {
+    if (value < rocksdb::StatsLevel::kAll && value >= 0) {
+        return true;
+    }
+
+    VLOG(3) << "Invalid value for --" << flagname << ": " << value;
+    return false;
+}
+
+DEFINE_int32(rocksdb_stats_level, 0,
+             "Statistics Level for RocksDB. Default is 0 (kExceptHistogramOrTimers)");
+DEFINE_validator(rocksdb_stats_level, &validateRocksdbStatsLevel);
+
+DEFINE_int32(rocksdb_stats_dump_period_sec, 0, "DBOptions::stats_dump_period_sec for RocksDB");
+
 /*
  * For these un-supported string options as below, will need to specify them with gflag.
  */

/* Copyright (c) 2018 vesoft inc. All rights reserved.
 *
 * This source code is licensed under Apache 2.0 License,
 * attached with Common Clause Condition 1.0, found in the LICENSES directory.
 */

#include "base/Base.h"
#include "kvstore/RocksEngineConfig.h"
#include "rocksdb/db.h"
#include "rocksdb/cache.h"
#include "rocksdb/convenience.h"
#include "rocksdb/utilities/options_util.h"
#include "rocksdb/slice_transform.h"

// [WAL]
DEFINE_bool(rocksdb_disable_wal,
            false,
            "Whether to disable the WAL in rocksdb");

// [DBOptions]
DEFINE_string(rocksdb_db_options,
              "",
              "DBOptions, each option will be given "
              "as <option_name>:<option_value> separated by ;");

// [CFOptions "default"]
DEFINE_string(rocksdb_column_family_options,
              "",
              "ColumnFamilyOptions, each option will be given "
              "as <option_name>:<option_value> separated by ;");

//  [TableOptions/BlockBasedTable "default"]
DEFINE_string(rocksdb_block_based_table_options,
              "",
              "BlockBasedTableOptions, each option will be given "
              "as <option_name>:<option_value> separated by ;");

DEFINE_int32(rocksdb_batch_size,
             4 * 1024,
             "default reserved bytes for one batch operation");

DEFINE_string(part_man_type,
              "memory",
              "memory, meta");
/*
 * For these un-supported string options as below, will need to specify them with gflag.
 */

// BlockBasedTable block_cache
DEFINE_int64(rocksdb_block_cache, 4,
             "The default block cache size used in BlockBasedTable. The unit is MB");

namespace nebula {
namespace kvstore {

rocksdb::Status initRocksdbOptions(rocksdb::Options &baseOpts) {
    rocksdb::Status s;
    rocksdb::DBOptions dbOpts;
    rocksdb::ColumnFamilyOptions cfOpts;
    rocksdb::BlockBasedTableOptions bbtOpts;
    s = GetDBOptionsFromString(rocksdb::DBOptions(),
            FLAGS_rocksdb_db_options, &dbOpts);
    if (!s.ok()) {
        return s;
    }

    s = GetColumnFamilyOptionsFromString(rocksdb::ColumnFamilyOptions(),
            FLAGS_rocksdb_column_family_options, &cfOpts);

    if (!s.ok()) {
        return s;
    }

    baseOpts = rocksdb::Options(dbOpts, cfOpts);

    s = GetBlockBasedTableOptionsFromString(rocksdb::BlockBasedTableOptions(),
            FLAGS_rocksdb_block_based_table_options, &bbtOpts);

    if (!s.ok()) {
        return s;
    }

    bbtOpts.block_cache = rocksdb::NewLRUCache(FLAGS_rocksdb_block_cache * 1024 * 1024);
    baseOpts.table_factory.reset(NewBlockBasedTableFactory(bbtOpts));
    baseOpts.create_if_missing = true;
    return s;
}

}  // namespace kvstore
}  // namespace nebula

I remember this option only control the interval for dumping stats into the LOG file, and if we want to get the stats in string, need to call `db->GetProperty()` to get the string version.


@@ -96,6 +96,8 @@ namespace thread_role
 			case nano::thread_role::name::signature_checking:
 				thread_role_name_string = "Signature check";
 				break;
+			case nano::thread_role::name::confirmation_height_processing:
+				thread_role_name_string = "Conf height";
 		}

 		/*

#include <iostream>
#include <nano/lib/utility.hpp>

namespace nano
{
seq_con_info_composite::seq_con_info_composite (const std::string & name) :
name (name)
{
}

bool seq_con_info_composite::is_composite () const
{
	return true;
}

void seq_con_info_composite::add_component (std::unique_ptr<seq_con_info_component> child)
{
	children.push_back (std::move (child));
}

const std::vector<std::unique_ptr<seq_con_info_component>> & seq_con_info_composite::get_children () const
{
	return children;
}

const std::string & seq_con_info_composite::get_name () const
{
	return name;
}

seq_con_info_leaf::seq_con_info_leaf (const seq_con_info & info) :
info (info)
{
}
bool seq_con_info_leaf::is_composite () const
{
	return false;
}
const seq_con_info & seq_con_info_leaf::get_info () const
{
	return info;
}

namespace thread_role
{
	/*
	 * nano::thread_role namespace
	 *
	 * Manage thread role
	 */
	static thread_local nano::thread_role::name current_thread_role = nano::thread_role::name::unknown;
	nano::thread_role::name get ()
	{
		return current_thread_role;
	}

	static std::string get_string (nano::thread_role::name role)
	{
		std::string thread_role_name_string;

		switch (role)
		{
			case nano::thread_role::name::unknown:
				thread_role_name_string = "<unknown>";
				break;
			case nano::thread_role::name::io:
				thread_role_name_string = "I/O";
				break;
			case nano::thread_role::name::work:
				thread_role_name_string = "Work pool";
				break;
			case nano::thread_role::name::packet_processing:
				thread_role_name_string = "Pkt processing";
				break;
			case nano::thread_role::name::alarm:
				thread_role_name_string = "Alarm";
				break;
			case nano::thread_role::name::vote_processing:
				thread_role_name_string = "Vote processing";
				break;
			case nano::thread_role::name::block_processing:
				thread_role_name_string = "Blck processing";
				break;
			case nano::thread_role::name::request_loop:
				thread_role_name_string = "Request loop";
				break;
			case nano::thread_role::name::wallet_actions:
				thread_role_name_string = "Wallet actions";
				break;
			case nano::thread_role::name::bootstrap_initiator:
				thread_role_name_string = "Bootstrap init";
				break;
			case nano::thread_role::name::voting:
				thread_role_name_string = "Voting";
				break;
			case nano::thread_role::name::signature_checking:
				thread_role_name_string = "Signature check";
				break;
		}

		/*
		 * We want to constrain the thread names to 15
		 * characters, since this is the smallest maximum
		 * length supported by the platforms we support
		 * (specifically, Linux)
		 */
		assert (thread_role_name_string.size () < 16);
		return (thread_role_name_string);
	}

	std::string get_string ()
	{
		return get_string (current_thread_role);
	}

	void set (nano::thread_role::name role)
	{
		auto thread_role_name_string (get_string (role));

		nano::thread_role::set_os_name (thread_role_name_string);

		nano::thread_role::current_thread_role = role;
	}
}
}

void nano::thread_attributes::set (boost::thread::attributes & attrs)
{
	auto attrs_l (&attrs);
	attrs_l->set_stack_size (8000000); //8MB
}

nano::thread_runner::thread_runner (boost::asio::io_context & io_ctx_a, unsigned service_threads_a)
{
	boost::thread::attributes attrs;
	nano::thread_attributes::set (attrs);
	for (auto i (0u); i < service_threads_a; ++i)
	{
		threads.push_back (boost::thread (attrs, [&io_ctx_a]() {
			nano::thread_role::set (nano::thread_role::name::io);
			try
			{
				io_ctx_a.run ();
			}
			catch (...)
			{
#ifndef NDEBUG
				/*
				 * In a release build, catch and swallow the
				 * io_context exception, in debug mode pass it
				 * on
				 */
				throw;
#endif
			}
		}));
	}
}

nano::thread_runner::~thread_runner ()
{
	join ();
}

void nano::thread_runner::join ()
{
	for (auto & i : threads)
	{
		if (i.joinable ())
		{
			i.join ();
		}
	}
}

/*
 * Backing code for "release_assert", which is itself a macro
 */
void release_assert_internal (bool check, const char * check_expr, const char * file, unsigned int line)
{
	if (check)
	{
		return;
	}

	std::cerr << "Assertion (" << check_expr << ") failed " << file << ":" << line << std::endl;
	abort ();
}

Looks like this falls through, should break.


@@ -145,13 +145,8 @@ std::unique_ptr<nebula::kvstore::KVStore> initKV(std::vector<nebula::HostAddr> p
     LOG(ERROR) << "Meta version is invalid";
     return nullptr;
   } else if (version == nebula::meta::MetaVersion::V1) {
-    auto ret = nebula::meta::MetaVersionMan::updateMetaV1ToV2(engine);
-    if (!ret.ok()) {
-      LOG(ERROR) << "Update meta from V1 to V2 failed " << ret;
-      return nullptr;
-    }
-
-    nebula::meta::MetaVersionMan::setMetaVersionToKV(engine, nebula::meta::MetaVersion::V2);
+    LOG(ERROR) << "Can't upgrade meta from V1 to V3";
+    return nullptr;
   } else if (version == nebula::meta::MetaVersion::V2) {
     auto ret = nebula::meta::MetaVersionMan::updateMetaV2ToV3(engine);
     if (!ret.ok()) {

/* Copyright (c) 2021 vesoft inc. All rights reserved.
 *
 * This source code is licensed under Apache 2.0 License.
 */

#include "MetaDaemonInit.h"

#include <folly/ssl/Init.h>
#include <thrift/lib/cpp2/server/ThriftServer.h>

#include "common/base/Base.h"
#include "common/base/SignalHandler.h"
#include "common/fs/FileUtils.h"
#include "common/hdfs/HdfsCommandHelper.h"
#include "common/hdfs/HdfsHelper.h"
#include "common/network/NetworkUtils.h"
#include "common/ssl/SSLConfig.h"
#include "common/thread/GenericThreadPool.h"
#include "common/utils/MetaKeyUtils.h"
#include "kvstore/NebulaStore.h"
#include "kvstore/PartManager.h"
#include "meta/ActiveHostsMan.h"
#include "meta/KVBasedClusterIdMan.h"
#include "meta/MetaServiceHandler.h"
#include "meta/MetaVersionMan.h"
#include "meta/http/MetaHttpDownloadHandler.h"
#include "meta/http/MetaHttpIngestHandler.h"
#include "meta/http/MetaHttpReplaceHostHandler.h"
#include "meta/processors/job/JobManager.h"
#include "meta/stats/MetaStats.h"
#include "webservice/Router.h"
#include "webservice/WebService.h"

#ifndef BUILD_STANDALONE
DEFINE_int32(num_io_threads, 16, "Number of IO threads");
DEFINE_int32(num_worker_threads, 32, "Number of workers");
DEFINE_string(data_path, "", "Root data path");
DEFINE_string(meta_server_addrs,
              "",
              "It is a list of IPs split by comma, used in cluster deployment"
              "the ips number is equal to the replica number."
              "If empty, it means it's a single node");
#else
DEFINE_int32(meta_num_io_threads, 16, "Number of IO threads");
DEFINE_int32(meta_num_worker_threads, 32, "Number of workers");
DEFINE_string(meta_data_path, "", "Root data path");
DECLARE_string(meta_server_addrs);  // use define from grap flags.
DECLARE_int32(ws_meta_http_port);
DECLARE_int32(ws_meta_h2_port);
#endif

using nebula::web::PathParams;

namespace nebula::meta {
const std::string kClusterIdKey = "__meta_cluster_id_key__";  // NOLINT
}  // namespace nebula::meta

nebula::ClusterID gClusterId = 0;
nebula::ClusterID& metaClusterId() {
  return gClusterId;
}

std::unique_ptr<nebula::kvstore::KVStore> initKV(std::vector<nebula::HostAddr> peers,
                                                 nebula::HostAddr localhost) {
  auto partMan = std::make_unique<nebula::kvstore::MemPartManager>();
  // The meta server has only one space (0), one part (0)
  partMan->addPart(nebula::kDefaultSpaceId, nebula::kDefaultPartId, std::move(peers));
#ifndef BUILD_STANDALONE
  int32_t numMetaIoThreads = FLAGS_num_io_threads;
  int32_t numMetaWorkerThreads = FLAGS_num_worker_threads;
#else
  int32_t numMetaIoThreads = FLAGS_meta_num_io_threads;
  int32_t numMetaWorkerThreads = FLAGS_meta_num_worker_threads;
#endif
  // folly IOThreadPoolExecutor
  auto ioPool = std::make_shared<folly::IOThreadPoolExecutor>(numMetaIoThreads);
  std::shared_ptr<apache::thrift::concurrency::ThreadManager> threadManager(
      apache::thrift::concurrency::PriorityThreadManager::newPriorityThreadManager(
          numMetaWorkerThreads));
  threadManager->setNamePrefix("executor");
  threadManager->start();
  nebula::kvstore::KVOptions options;
#ifndef BUILD_STANDALONE
  auto absolute = boost::filesystem::absolute(FLAGS_data_path);
#else
  auto absolute = boost::filesystem::absolute(FLAGS_meta_data_path);
#endif
  options.dataPaths_ = {absolute.string()};
  options.partMan_ = std::move(partMan);
  auto kvstore = std::make_unique<nebula::kvstore::NebulaStore>(
      std::move(options), ioPool, localhost, threadManager);
  if (!(kvstore->init())) {
    LOG(ERROR) << "Nebula store init failed";
    return nullptr;
  }

  auto engineRet = kvstore->part(nebula::kDefaultSpaceId, nebula::kDefaultPartId);
  if (!nebula::ok(engineRet)) {
    LOG(ERROR) << "Get nebula store engine failed";
    return nullptr;
  }

  auto engine = nebula::value(engineRet)->engine();
  LOG(INFO) << "Waiting for the leader elected...";
  nebula::HostAddr leader;
  while (true) {
    auto ret = kvstore->partLeader(nebula::kDefaultSpaceId, nebula::kDefaultPartId);
    if (!nebula::ok(ret)) {
      LOG(ERROR) << "Nebula store init failed";
      return nullptr;
    }
    leader = nebula::value(ret);
    if (leader != nebula::HostAddr("", 0)) {
      break;
    }
    LOG(INFO) << "Leader has not been elected, sleep 1s";
    sleep(1);
  }

  gClusterId =
      nebula::meta::ClusterIdMan::getClusterIdFromKV(kvstore.get(), nebula::meta::kClusterIdKey);
  if (gClusterId == 0) {
    if (leader == localhost) {
      LOG(INFO) << "I am leader, create cluster Id";
      gClusterId = nebula::meta::ClusterIdMan::create(FLAGS_meta_server_addrs);
      if (!nebula::meta::ClusterIdMan::persistInKV(
              kvstore.get(), nebula::meta::kClusterIdKey, gClusterId)) {
        LOG(ERROR) << "Persist cluster failed!";
        return nullptr;
      }
    } else {
      LOG(INFO) << "I am follower, wait for the leader's clusterId";
      while (gClusterId == 0) {
        LOG(INFO) << "Waiting for the leader's clusterId";
        sleep(1);
        gClusterId = nebula::meta::ClusterIdMan::getClusterIdFromKV(kvstore.get(),
                                                                    nebula::meta::kClusterIdKey);
      }
    }
  }

  auto version = nebula::meta::MetaVersionMan::getMetaVersionFromKV(kvstore.get());
  LOG(INFO) << "Get meta version is " << static_cast<int32_t>(version);
  if (version == nebula::meta::MetaVersion::UNKNOWN) {
    LOG(ERROR) << "Meta version is invalid";
    return nullptr;
  } else if (version == nebula::meta::MetaVersion::V1) {
    auto ret = nebula::meta::MetaVersionMan::updateMetaV1ToV2(engine);
    if (!ret.ok()) {
      LOG(ERROR) << "Update meta from V1 to V2 failed " << ret;
      return nullptr;
    }

    nebula::meta::MetaVersionMan::setMetaVersionToKV(engine, nebula::meta::MetaVersion::V2);
  } else if (version == nebula::meta::MetaVersion::V2) {
    auto ret = nebula::meta::MetaVersionMan::updateMetaV2ToV3(engine);
    if (!ret.ok()) {
      LOG(ERROR) << "Update meta from V2 to V3 failed " << ret;
      return nullptr;
    }

    nebula::meta::MetaVersionMan::setMetaVersionToKV(engine, nebula::meta::MetaVersion::V3);
  }

  LOG(INFO) << "Nebula store init succeeded, clusterId " << gClusterId;
  return kvstore;
}

nebula::Status initWebService(nebula::WebService* svc,
                              nebula::kvstore::KVStore* kvstore,
                              nebula::hdfs::HdfsCommandHelper* helper,
                              nebula::thread::GenericThreadPool* pool) {
  LOG(INFO) << "Starting Meta HTTP Service";
  auto& router = svc->router();
  router.get("/download-dispatch").handler([kvstore, helper, pool](PathParams&&) {
    auto handler = new nebula::meta::MetaHttpDownloadHandler();
    handler->init(kvstore, helper, pool);
    return handler;
  });
  router.get("/ingest-dispatch").handler([kvstore, pool](PathParams&&) {
    auto handler = new nebula::meta::MetaHttpIngestHandler();
    handler->init(kvstore, pool);
    return handler;
  });
  router.get("/replace").handler([kvstore](PathParams&&) {
    auto handler = new nebula::meta::MetaHttpReplaceHostHandler();
    handler->init(kvstore);
    return handler;
  });
#ifndef BUILD_STANDALONE
  return svc->start();
#else
  return svc->start(FLAGS_ws_meta_http_port, FLAGS_ws_meta_h2_port);
#endif
}

Only V1 or V3 is return from `getMetaVersionFromKV`. So where do we call `updateMetaV2ToV3`?


@@ -120,6 +120,8 @@ func writeNextUnescapedRune(s string, builder *strings.Builder) (width int, err
 			r = '\\'
 		case '"':
 			r = '"'
+		case '$':
+			r = '$'
 		case 'x':
 			// Decode two hex chars as a single byte
 			if len(s[width:]) < 2 {

package parser

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"

	"github.com/influxdata/flux/ast"
)

// ParseTime will parse a time literal from a string.
func ParseTime(lit string) (time.Time, error) {
	if !strings.Contains(lit, "T") {
		// This is a date.
		return time.Parse("2006-01-02", lit)
	}
	// todo(jsternberg): need to also parse when there is no time offset.
	return time.Parse(time.RFC3339Nano, lit)
}

// MustParseTime parses a time literal and panics in the case of an error.
func MustParseTime(lit string) time.Time {
	ts, err := ParseTime(lit)
	if err != nil {
		panic(err)
	}
	return ts
}

// ParseDuration will convert a string into components of the duration.
func ParseDuration(lit string) ([]ast.Duration, error) {
	var values []ast.Duration
	for len(lit) > 0 {
		n := 0
		for n < len(lit) {
			ch, size := utf8.DecodeRuneInString(lit[n:])
			if size == 0 {
				panic("invalid rune in duration")
			}

			if !unicode.IsDigit(ch) {
				break
			}
			n += size
		}

		magnitude, err := strconv.ParseInt(lit[:n], 10, 64)
		if err != nil {
			return nil, err
		}
		lit = lit[n:]

		n = 0
		for n < len(lit) {
			ch, size := utf8.DecodeRuneInString(lit[n:])
			if size == 0 {
				panic("invalid rune in duration")
			}

			if !unicode.IsLetter(ch) {
				break
			}
			n += size
		}
		unit := lit[:n]
		if unit == "µs" {
			unit = "us"
		}
		values = append(values, ast.Duration{
			Magnitude: magnitude,
			Unit:      unit,
		})
		lit = lit[n:]
	}
	return values, nil
}

// ParseString removes quotes and unescapes the string literal.
func ParseString(lit string) (string, error) {
	if len(lit) < 2 || lit[0] != '"' || lit[len(lit)-1] != '"' {
		return "", fmt.Errorf("invalid syntax")
	}
	lit = lit[1 : len(lit)-1]
	var (
		builder    strings.Builder
		width, pos int
		err        error
	)
	builder.Grow(len(lit))
	for pos < len(lit) {
		width, err = writeNextUnescapedRune(lit[pos:], &builder)
		if err != nil {
			return "", err
		}
		pos += width
	}
	return builder.String(), nil
}

// writeNextUnescapedRune writes a rune to builder from s.
// The rune is the next decoded UTF-8 rune with escaping rules applied.
func writeNextUnescapedRune(s string, builder *strings.Builder) (width int, err error) {
	var r rune
	r, width = utf8.DecodeRuneInString(s)
	if r == '\\' {
		next, w := utf8.DecodeRuneInString(s[width:])
		width += w
		switch next {
		case 'n':
			r = '\n'
		case 'r':
			r = '\r'
		case 't':
			r = '\t'
		case '\\':
			r = '\\'
		case '"':
			r = '"'
		case 'x':
			// Decode two hex chars as a single byte
			if len(s[width:]) < 2 {
				return 0, fmt.Errorf("invalid byte value %q", s[width:])
			}
			ch1, ok1 := fromHexChar(s[width])
			ch2, ok2 := fromHexChar(s[width+1])
			if !ok1 || !ok2 {
				return 0, fmt.Errorf("invalid byte value %q", s[width:])
			}
			builder.WriteByte((ch1 << 4) | ch2)
			return width + 2, nil
		default:
			return 0, fmt.Errorf("invalid escape character %q", next)
		}
	}
	// sanity check before writing the rune
	if width > 0 {
		builder.WriteRune(r)
	}
	return
}

// fromHexChar converts a hex character into its value and a success flag.
func fromHexChar(c byte) (byte, bool) {
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}
	return 0, false
}

// ParseRegexp converts text surrounded by forward slashes into a regular expression.
func ParseRegexp(lit string) (*regexp.Regexp, error) {
	if len(lit) < 3 {
		return nil, fmt.Errorf("regexp must be at least 3 characters")
	}

	if lit[0] != '/' {
		return nil, fmt.Errorf("regexp literal must start with a slash")
	} else if lit[len(lit)-1] != '/' {
		return nil, fmt.Errorf("regexp literal must end with a slash")
	}

	expr := lit[1 : len(lit)-1]
	if index := strings.Index(expr, "\\/"); index != -1 {
		expr = strings.Replace(expr, "\\/", "/", -1)
	}
	return regexp.Compile(expr)
}

Does this needs to check that the next rune is `{`? Otherwise it will incorrectly accept `\$` as a valid escape sequence? Or does the scanner ensure its always `${` together?


@@ -36,6 +36,7 @@ func NewInstaller(dc dynamic.Interface, config map[string]string, paths ...strin
 	}

 	for i, p := range paths {
+		log.Println("processing yaml folder", p)
 		paths[i] = ParseTemplates(p, config)
 	}
 	path := strings.Join(paths, ",")

/*
Copyright 2019 Google LLC

Licensed 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.
*/

package e2e

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	yaml "github.com/jcrossley3/manifestival/pkg/manifestival"
	"k8s.io/client-go/dynamic"
)

func NewInstaller(dc dynamic.Interface, config map[string]string, paths ...string) *Installer {
	if len(paths) == 0 || (len(paths) == 1 && paths[0] == "") {
		// default to ko path:
		paths[0] = "/var/run/ko/install"
	}

	for i, p := range paths {
		paths[i] = ParseTemplates(p, config)
	}
	path := strings.Join(paths, ",")

	manifest, err := yaml.NewYamlManifest(path, true, dc)
	if err != nil {
		panic(err)
	}
	return &Installer{dc: dc, manifest: manifest}
}

func ParseTemplates(path string, config map[string]string) string {
	dir, err := ioutil.TempDir("", "processed_yaml")
	if err != nil {
		panic(err)
	}

	err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(info.Name(), "yaml") {

			t, err := template.ParseFiles(path)
			if err != nil {
				return err
			}
			tmpfile, err := ioutil.TempFile(dir, strings.Replace(info.Name(), ".yaml", "-*.yaml", 1))
			if err != nil {
				log.Fatal(err)
			}
			err = t.Execute(tmpfile, config)
			if err != nil {
				log.Print("execute: ", err)
				return err
			}
			_ = tmpfile.Close()
		}
		return nil
	})
	log.Print("new files in ", dir)
	if err != nil {
		panic(err)
	}
	return dir
}

type Installer struct {
	dc dynamic.Interface

	manifest yaml.Manifest
}

func (r *Installer) Do(verb string) error {
	switch strings.ToLower(verb) {
	case "create", "setup", "install", "apply", "start":
		return r.manifest.ApplyAll()
	case "delete", "teardown", "uninstall", "unapply", "stop":
		return r.manifest.DeleteAll()
	default:
		return fmt.Errorf("unknown verb: %s", verb)
	}
}


@@ -29,13 +29,16 @@ package authorization
 import (
 	"crypto/x509/pkix"

+	"google.golang.org/grpc/credentials"
+
 	"go.temporal.io/server/common/service/config"
 )

 // Authentication information from subject's JWT token or/and mTLS certificate
 type AuthInfo struct {
-	authToken  string
-	tlsSubject *pkix.Name
+	AuthToken     string
+	TlsSubject    *pkix.Name
+	TLSConnection *credentials.TLSInfo
 }

 // Converts authorization info of a subject into Temporal claims (permissions) for authorization

// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//go:generate mockgen -copyright_file ../../LICENSE -package $GOPACKAGE -source $GOFILE -destination claim_mapper_mock.go -self_package go.temporal.io/server/common/authorization

package authorization

import (
	"crypto/x509/pkix"

	"go.temporal.io/server/common/service/config"
)

// Authentication information from subject's JWT token or/and mTLS certificate
type AuthInfo struct {
	authToken  string
	tlsSubject *pkix.Name
}

// Converts authorization info of a subject into Temporal claims (permissions) for authorization
type ClaimMapper interface {
	GetClaims(authInfo *AuthInfo) (*Claims, error)
}

// No-op claim mapper that gives system level admin permission to everybody
type noopClaimMapper struct{}

var _ ClaimMapper = (*noopClaimMapper)(nil)

func NewNoopClaimMapper(_ *config.Config) ClaimMapper {
	return &noopClaimMapper{}
}

func (*noopClaimMapper) GetClaims(_ *AuthInfo) (*Claims, error) {
	return &Claims{system: RoleAdmin}, nil
}

[Nit] let's be consistent with below casing and call it TLSSubject instead?


@@ -115,15 +115,7 @@ func (c call) endStats(elapsed time.Duration, err error, isApplicationError bool
 		return
 	}

-	if !yarpcerror.IsStatus(err) {
-		c.edge.serverErrLatencies.Observe(elapsed)
-		if counter, err := c.edge.serverFailures.Get(_error, "unknown_internal_yarpc"); err == nil {
-			counter.Inc()
-		}
-		return
-	}
-
-	errCode := yarpcerror.FromError(err).Code()
+	errCode := yarpcerror.GetInfo(err).Code
 	switch errCode {
 	case yarpcerror.CodeCancelled,
 		yarpcerror.CodeInvalidArgument,

// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package internalyarpcobservability

import (
	"context"
	"time"

	"go.uber.org/yarpc/v2"
	"go.uber.org/yarpc/v2/yarpcerror"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

const (
	_error              = "error"
	_successfulInbound  = "Handled inbound request."
	_successfulOutbound = "Made outbound call."
	_errorInbound       = "Error handling inbound request."
	_errorOutbound      = "Error making outbound call."
)

// A call represents a single RPC along an edge.
//
// To prevent allocating on the heap on the request path, it's a value instead
// of a pointer.
type call struct {
	edge    *edge
	extract ContextExtractor
	fields  [5]zapcore.Field

	started   time.Time
	ctx       context.Context
	req       *yarpc.Request
	rpcType   yarpc.Type
	direction directionName
}

func (c call) End(err error) {
	c.EndWithAppError(err, false)
}

func (c call) EndWithAppError(err error, isApplicationError bool) {
	elapsed := _timeNow().Sub(c.started)
	c.endLogs(elapsed, err, isApplicationError)
	c.endStats(elapsed, err, isApplicationError)
}

func (c call) endLogs(elapsed time.Duration, err error, isApplicationError bool) {
	var ce *zapcore.CheckedEntry
	if err == nil && !isApplicationError {
		msg := _successfulInbound
		if c.direction != _directionInbound {
			msg = _successfulOutbound
		}
		ce = c.edge.logger.Check(zap.DebugLevel, msg)
	} else {
		msg := _errorInbound
		if c.direction != _directionInbound {
			msg = _errorOutbound
		}
		ce = c.edge.logger.Check(zap.ErrorLevel, msg)
	}

	if ce == nil {
		return
	}

	fields := c.fields[:0]
	fields = append(fields, zap.String("rpcType", c.rpcType.String()))
	fields = append(fields, zap.Duration("latency", elapsed))
	fields = append(fields, zap.Bool("successful", err == nil && !isApplicationError))
	fields = append(fields, c.extract(c.ctx))
	if isApplicationError {
		fields = append(fields, zap.String(_error, "application_error"))
	} else {
		fields = append(fields, zap.Error(err))
	}
	ce.Write(fields...)
}

func (c call) endStats(elapsed time.Duration, err error, isApplicationError bool) {
	// TODO: We need a much better way to distinguish between caller and server
	// errors. See T855583.
	c.edge.calls.Inc()
	if err == nil && !isApplicationError {
		c.edge.successes.Inc()
		c.edge.latencies.Observe(elapsed)
		return
	}
	// For now, assume that all application errors are the caller's fault.
	if isApplicationError {
		c.edge.callerErrLatencies.Observe(elapsed)
		if counter, err := c.edge.callerFailures.Get(_error, "application_error"); err == nil {
			counter.Inc()
		}
		return
	}

	if !yarpcerror.IsStatus(err) {
		c.edge.serverErrLatencies.Observe(elapsed)
		if counter, err := c.edge.serverFailures.Get(_error, "unknown_internal_yarpc"); err == nil {
			counter.Inc()
		}
		return
	}

	errCode := yarpcerror.FromError(err).Code()
	switch errCode {
	case yarpcerror.CodeCancelled,
		yarpcerror.CodeInvalidArgument,
		yarpcerror.CodeNotFound,
		yarpcerror.CodeAlreadyExists,
		yarpcerror.CodePermissionDenied,
		yarpcerror.CodeFailedPrecondition,
		yarpcerror.CodeAborted,
		yarpcerror.CodeOutOfRange,
		yarpcerror.CodeUnimplemented,
		yarpcerror.CodeUnauthenticated:
		c.edge.callerErrLatencies.Observe(elapsed)
		if counter, err := c.edge.callerFailures.Get(_error, errCode.String()); err == nil {
			counter.Inc()
		}
		return
	case yarpcerror.CodeUnknown,
		yarpcerror.CodeDeadlineExceeded,
		yarpcerror.CodeResourceExhausted,
		yarpcerror.CodeInternal,
		yarpcerror.CodeUnavailable,
		yarpcerror.CodeDataLoss:
		c.edge.serverErrLatencies.Observe(elapsed)
		if counter, err := c.edge.serverFailures.Get(_error, errCode.String()); err == nil {
			counter.Inc()
		}
		return
	}
	// If this code is executed we've hit an error code outside the usual error
	// code range, so we'll just log the string representation of that code.
	c.edge.serverErrLatencies.Observe(elapsed)
	if counter, err := c.edge.serverFailures.Get(_error, errCode.String()); err == nil {
		counter.Inc()
	}
}

Do we have a changelog for expected behaviour changes yarpcv1 to v2? Would be good to attach that `unknown_internal_yarpc` metrics now change to `unknown`


@@ -62,6 +62,13 @@ func (s *Server) Start(ctx context.Context) error {
 		return errors.Wrap(err, "error when subscribe to block")
 	}

+	// sync genesis block
+	genesisBlk, err := s.bc.GetBlockByHeight(0)
+	if err != nil {
+		return errors.Wrap(err, "error when get genesis block")
+	}
+	s.idx.BuildIndex(genesisBlk)
+
 	go func() {
 		for {
 			select {

// Copyright (c) 2018 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.

package indexservice

import (
	"github.com/pkg/errors"
	"golang.org/x/net/context"

	"encoding/hex"
	"github.com/iotexproject/iotex-core/blockchain"
	"github.com/iotexproject/iotex-core/config"
	"github.com/iotexproject/iotex-core/db/rds"
)

// Server is the container of the index service
type Server struct {
	cfg     *config.Config
	idx     *Indexer
	bc      blockchain.Blockchain
	blockCh chan *blockchain.Block
}

// NewServer instantiates an index service
func NewServer(
	cfg *config.Config,
	bc blockchain.Blockchain,
) *Server {
	return &Server{
		cfg: cfg,
		idx: &Indexer{
			cfg:                cfg.Indexer,
			rds:                nil,
			hexEncodedNodeAddr: "",
		},
		bc: bc,
	}
}

// Start starts the explorer server
func (s *Server) Start(ctx context.Context) error {
	addr := s.cfg.Indexer.NodeAddr
	if addr == "" {
		blockAddr, err := s.cfg.BlockchainAddress()
		if err != nil {
			return errors.Wrap(err, "error when get the blockchain address")
		}
		addr = hex.EncodeToString(blockAddr.Bytes()[:])
	}
	s.idx.hexEncodedNodeAddr = addr

	s.idx.rds = rds.NewAwsRDS(&s.cfg.DB.RDS)
	if err := s.idx.rds.Start(ctx); err != nil {
		return errors.Wrap(err, "error when start rds store")
	}

	s.blockCh = make(chan *blockchain.Block)
	if err := s.bc.SubscribeBlockCreation(s.blockCh); err != nil {
		return errors.Wrap(err, "error when subscribe to block")
	}

	go func() {
		for {
			select {
			case blk := <-s.blockCh:
				s.idx.BuildIndex(blk)
			}
		}
	}()

	return nil
}

// Stop stops the explorer server
func (s *Server) Stop(ctx context.Context) error {
	if err := s.idx.rds.Stop(ctx); err != nil {
		return errors.Wrap(err, "error when shutting down explorer http server")
	}
	if err := s.bc.UnSubscribeBlockCreation(s.blockCh); err != nil {
		return errors.Wrap(err, "error when un subscribe block creation")
	}
	close(s.blockCh)
	return nil
}

// Indexer return indexer interface
func (s *Server) Indexer() *Indexer { return s.idx }

Error return value of `s.idx.BuildIndex` is not checked


@@ -35,3 +35,7 @@ func (s *stub) ChangedLines() (map[string][]int, error) {
 func (s *stub) Checkout(revision string) error {
 	return fmt.Errorf("Unknown SCM, can't checkout")
 }
+
+func (s *stub) CurrentRevDate(format string) string {
+	return "<unknown>"
+}

package scm

import "fmt"

type stub struct{}

func (s *stub) DescribeIdentifier(sha string) string {
	return "<unknown>"
}

func (s *stub) CurrentRevIdentifier() string {
	return "<unknown>"
}

func (s *stub) ChangesIn(diffSpec string, relativeTo string) []string {
	return nil
}

func (s *stub) ChangedFiles(fromCommit string, includeUntracked bool, relativeTo string) []string {
	return nil
}

func (s *stub) IgnoreFile(name string) error {
	return fmt.Errorf("Don't know how to mark %s as ignored", name)
}

func (s *stub) Remove(names []string) error {
	return fmt.Errorf("Unknown SCM, can't remove files")
}

func (s *stub) ChangedLines() (map[string][]int, error) {
	return nil, fmt.Errorf("Unknown SCM, can't calculate changed lines")
}

func (s *stub) Checkout(revision string) error {
	return fmt.Errorf("Unknown SCM, can't checkout")
}

Don't strongly mind which but let's pick one of either `<unknown>` or `UNKNOWN` (as in the git impl when it can't be determined)


@@ -79,7 +79,7 @@ func getStatsForProcess(name string, pcpu *float64, rss, vss *int64, pid *int) (
 			return nil
 		} else {
 			// something went wrong executing the command
-			Debugf("exec failure: %s\n", string(out))
+			// Debugf("exec failure: %s\n", string(out))
 			return errors.New(fmt.Sprintf("typeperf failed: %v", err))
 		}
 	}

// Copyright 2015-2016 Apcera Inc. All rights reserved.

package pse

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

// cache the image name to optimize repeated calls
var imageName string
var imageLock sync.Mutex

// parseValues parses the results of data returned by typeperf.exe.  This
// is a series of comma delimited quoted strings, containing date time,
// pid, pcpu, rss, and vss.  All numeric values are floating point.
// eg: "04/17/2016 15.38.00.016", "5123.00000", "1.23400", "123.00000", "123.00000"
func parseValues(line string, pid *int, pcpu *float64, rss, vss *int64) (err error) {
	values := strings.Split(line, ",")
	if len(values) < 4 {
		return errors.New("Invalid result.")
	}
	// values[0] will be date, time, ignore them
	// parse the pid
	fVal, err := strconv.ParseFloat(strings.Trim(values[1], "\""), 64)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to parse pid: %s", values[1]))
	}
	*pid = int(fVal)

	// parse pcpu
	*pcpu, err = strconv.ParseFloat(strings.Trim(values[2], "\""), 64)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to parse percent cpu: %s", values[2]))
	}

	// parse private working set (rss)
	fVal, err = strconv.ParseFloat(strings.Trim(values[3], "\""), 64)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to parse working set: %s", values[3]))
	}
	*rss = int64(fVal)

	// parse virtual bytes (vsz)
	fVal, err = strconv.ParseFloat(strings.Trim(values[4], "\""), 64)
	if err != nil {
		return errors.New(fmt.Sprintf("Unable to parse virtual bytes: %s", values[4]))
	}
	*vss = int64(fVal)

	return nil
}

// getStatsForProcess retrieves process information for a given instance name.
// typeperf.exe is the windows native command line utility to get pcpu, rss,
// and vsz equivalents through queries of performance counters.
// An alternative is to map the Pdh* native windows API from pdh.dll,
// and call those APIs directly - this is a simpler and cleaner approach.
func getStatsForProcess(name string, pcpu *float64, rss, vss *int64, pid *int) (err error) {
	// query the counters using typeperf. "-sc","1" requests one
	// set of data (versus continuous monitoring)
	out, err := exec.Command("typeperf.exe",
		fmt.Sprintf("\\Process(%s)\\ID Process", name),
		fmt.Sprintf("\\Process(%s)\\%% Processor Time", name),
		fmt.Sprintf("\\Process(%s)\\Working Set - Private", name),
		fmt.Sprintf("\\Process(%s)\\Virtual Bytes", name),
		"-sc", "1").Output()
	if err != nil {
		// Signal that the command ran, but the image instance was not found
		// through a PID of -1.
		if strings.Contains(string(out), "The data is not valid") {
			*pid = -1
			return nil
		} else {
			// something went wrong executing the command
			Debugf("exec failure: %s\n", string(out))
			return errors.New(fmt.Sprintf("typeperf failed: %v", err))
		}
	}

	results := strings.Split(string(out), "\r\n")
	// results[0] = newline
	// results[1] = headers
	// results[2] = values
	// ignore the rest...
	if len(results) < 3 {
		return errors.New(fmt.Sprintf("unexpected results from typeperf"))
	}
	if err = parseValues(results[2], pid, pcpu, rss, vss); err != nil {
		return err
	}
	return nil
}

// getProcessImageName returns the name of the process image, as expected by
// typeperf.
func getProcessImageName() (name string) {
	name = filepath.Base(os.Args[0])
	name = strings.TrimRight(name, ".exe")
	return
}

// procUsage retrieves process cpu and memory information.
// Under the hood, typeperf is called.  Notably, typeperf cannot search
// using a pid, but instead uses a somewhat volatile process image name.
// If there is more than one instance, "#<instancecount>" is appended to
// the image name. Wildcard filters are supported, but result in a very
// complex data set to parse.
func ProcUsage(pcpu *float64, rss, vss *int64) error {
	var ppid int = -1

	imageLock.Lock()
	name := imageName
	imageLock.Unlock()

	// Get the pid to retrieve the right set of information for this process.
	procPid := os.Getpid()

	// if we have cached the image name, try that first
	if name != "" {
		err := getStatsForProcess(name, pcpu, rss, vss, &ppid)
		if err != nil {
			return err
		}
		// If the instance name's pid matches ours, we're done.
		// Otherwise, this instance has been renamed, which is possible
		// as other process instances start and stop on the system.
		if ppid == procPid {
			return nil
		}
	}
	// If we get here, the instance name is invalid (nil, or out of sync)
	// Query pid and counters until the correct image name is found and
	// cache it.  This is optimized for one or two instances on a windows
	// node. An alternative is using a wildcard to first lookup up pids,
	// and parse those to find instance name, then lookup the
	// performance counters.
	prefix := getProcessImageName()
	for i := 0; ppid != procPid; i++ {
		name = fmt.Sprintf("%s#%d", prefix, i)
		err := getStatsForProcess(name, pcpu, rss, vss, &ppid)
		if err != nil {
			return err
		}

		// Bail out if an image name is not found.
		if ppid < 0 {
			break
		}

		// if the pids equal, this is the right process and cache our
		// image name
		if ppid == procPid {
			imageLock.Lock()
			imageName = name
			imageLock.Unlock()
			break
		}
	}
	if ppid < 0 {
		return errors.New("unable to retrieve process counters")
	}
	return nil
}

Just remove instead of leaving the code here.


@@ -85,6 +85,8 @@ func register(reg transport.Registry) {
 	json.Register(reg, json.Procedure("unexpected-error", UnexpectedError))
 	json.Register(reg, json.Procedure("bad-response", BadResponse))
 	json.Register(reg, json.Procedure("phone", Phone))
+	json.Register(reg, json.Procedure("sleep", Sleep))

 	raw.Register(reg, raw.Procedure("sleep/raw", SleepRaw))
+	raw.Register(reg, raw.Procedure("waitfortimeout/raw", WaitForTimeoutRaw))
 }

// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package yarpc

import (
	"fmt"
	"log"

	"github.com/yarpc/yarpc-go"
	"github.com/yarpc/yarpc-go/crossdock/thrift/echo/yarpc/echoserver"
	"github.com/yarpc/yarpc-go/crossdock/thrift/gauntlet/yarpc/secondserviceserver"
	"github.com/yarpc/yarpc-go/crossdock/thrift/gauntlet/yarpc/thrifttestserver"
	"github.com/yarpc/yarpc-go/encoding/json"
	"github.com/yarpc/yarpc-go/encoding/raw"
	"github.com/yarpc/yarpc-go/encoding/thrift"
	"github.com/yarpc/yarpc-go/transport"
	"github.com/yarpc/yarpc-go/transport/http"
	tch "github.com/yarpc/yarpc-go/transport/tchannel"

	"github.com/uber/tchannel-go"
)

var dispatcher yarpc.Dispatcher

// Start starts the test server that clients will make requests to
func Start() {
	ch, err := tchannel.NewChannel("yarpc-test", nil)
	if err != nil {
		log.Fatalln("couldn't create tchannel: %v", err)
	}

	dispatcher = yarpc.NewDispatcher(yarpc.Config{
		Name: "yarpc-test",
		Inbounds: []transport.Inbound{
			http.NewInbound(":8081"),
			tch.NewInbound(ch, tch.ListenAddr(":8082")),
		},
	})

	register(dispatcher)

	if err := dispatcher.Start(); err != nil {
		fmt.Println("error:", err.Error())
	}
}

// Stop stops running the RPC test subject
func Stop() {
	if dispatcher == nil {
		return
	}
	if err := dispatcher.Stop(); err != nil {
		fmt.Println("failed to stop:", err.Error())
	}
}

func register(reg transport.Registry) {
	raw.Register(reg, raw.Procedure("echo/raw", EchoRaw))
	json.Register(reg, json.Procedure("echo", EchoJSON))

	// NOTE(abg): Enveloping is disabled in old cross-language tests until the
	// other YARPC implementations catch up.
	thrift.Register(reg, echoserver.New(EchoThrift{}), thrift.DisableEnveloping)
	thrift.Register(reg, thrifttestserver.New(thriftTest{}), thrift.DisableEnveloping)
	thrift.Register(reg, secondserviceserver.New(secondService{}), thrift.DisableEnveloping)

	json.Register(reg, json.Procedure("unexpected-error", UnexpectedError))
	json.Register(reg, json.Procedure("bad-response", BadResponse))
	json.Register(reg, json.Procedure("phone", Phone))

	raw.Register(reg, raw.Procedure("sleep/raw", SleepRaw))
}

Why have the term `raw` in the procedure name? Why not just `waitfortimeout`?


@@ -1,4 +1,4 @@
-// Copyright (c) 2020 Tigera, Inc. All rights reserved.
+// Copyright (c) 2020-2021 Tigera, Inc. All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.

// Copyright (c) 2020 Tigera, Inc. All rights reserved.
//
// Licensed 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.

// Copyright (c) 2020  All rights reserved.

package ut_test

import (
	"os"
	"testing"
)

func TestMain(m *testing.M) {
	initMapsOnce()
	cleanUpMaps()
	rc := m.Run()
	cleanUpMaps()
	os.Exit(rc)
}

Should revert this copyright change, when the file isn't changing in any other way.


@@ -1,4 +1,4 @@
-package aws_test
+package aws

 import (
 	"fmt"

package aws_test

import (
	"fmt"
	"os"
	"testing"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/opsworks"
	"github.com/libopenstorage/openstorage/pkg/storageops"
	"github.com/libopenstorage/openstorage/pkg/storageops/aws"
	"github.com/libopenstorage/openstorage/pkg/storageops/test"
	uuid "github.com/satori/go.uuid"
)

const (
	newDiskSizeInGB = 10
	newDiskPrefix   = "openstorage-test"
)

var diskName = fmt.Sprintf("%s-%s", newDiskPrefix, uuid.NewV4())

func TestAll(t *testing.T) {
	drivers := make(map[string]storageops.Ops)
	diskTemplates := make(map[string]map[string]interface{})

	if d, err := aws.NewEnvClient(); err != aws.ErrAWSEnvNotAvailable {
		volType := opsworks.VolumeTypeGp2
		volSize := int64(newDiskSizeInGB)
		zone := os.Getenv("AWS_ZONE")
		ebsVol := &ec2.Volume{
			AvailabilityZone: &zone,
			VolumeType:       &volType,
			Size:             &volSize,
		}
		drivers[d.Name()] = d
		diskTemplates[d.Name()] = map[string]interface{}{
			diskName: ebsVol,
		}
	} else {
		fmt.Printf("skipping AWS tests as environment is not set...\n")
	}

	test.RunTest(drivers, diskTemplates, t)
}

@lpabon having a separate package name `aws_test` allows to test the package as if the tester was an external package. If the test package name is the same as the package being tested, the test package can also use methods and variables not exposed to the eventual user.


@@ -94,8 +94,19 @@ func New(impl Interface, opts ...<$thrift>.RegisterOption) []<$transport>.Proced
 	}

 	procedures := make([]<$transport>.Procedure, 0, <len .Functions>)
-	<if .Parent> procedures = append(procedures, <import .ParentServerPackagePath>.New(impl, opts...)...)
-	<end>         procedures = append(procedures, <$thrift>.BuildProcedures(service, opts...)...)
+	<if .Parent>
+	procedures = append(
+		procedures,
+		<import .ParentServerPackagePath>.New(
+			impl,
+			append(
+				opts,
+				<$thrift>.Named("<.Name>"),
+			)...,
+		)...,
+	)
+	<end ->
+	procedures = append(procedures, <$thrift>.BuildProcedures(service, opts...)...)
 	return procedures
 }

// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"path/filepath"

	"go.uber.org/thriftrw/plugin"
)

const serverTemplate = `
// Code generated by thriftrw-plugin-yarpc
// @generated

<$pkgname := printf "%sserver" (lower .Name)>
package <$pkgname>

<$thrift    := import "go.uber.org/yarpc/encoding/thrift">
<$transport := import "go.uber.org/yarpc/api/transport">

<$contextImportPath   := .ContextImportPath>
<$onewayWrapperImport := .OnewayWrapperImport>
<$onewayWrapperFunc   := .OnewayWrapperFunc>
<$unaryWrapperImport  := .UnaryWrapperImport>
<$unaryWrapperFunc    := .UnaryWrapperFunc>

</* Note that we import things like "context" inside loops rather than at the
    top-level because they will end up unused if the service does not have any
    functions.
 */>

// Interface is the server-side interface for the <.Name> service.
type Interface interface {
	<if .Parent><import .ParentServerPackagePath>.Interface
	<end>
	<range .Functions>
		<$context := import $contextImportPath>
		<.Name>(
			ctx <$context>.Context, <range .Arguments>
			<.Name> <formatType .Type>,<end>
		)<if .OneWay> error
		<else if .ReturnType> (<formatType .ReturnType>, error)
		<else> error
		<end>
	<end>
}

<$module := .Module>

// New prepares an implementation of the <.Name> service for
// registration.
//
// 	handler := <.Name>Handler{}
// 	dispatcher.Register(<$pkgname>.New(handler))
func New(impl Interface, opts ...<$thrift>.RegisterOption) []<$transport>.Procedure {
	<if .Functions>h := handler{impl}<end>
	service := <$thrift>.Service{
		Name: "<.Name>",
		Methods: []<$thrift>.Method{
		<range .Functions>
			<$thrift>.Method{
				Name: "<.ThriftName>",
				HandlerSpec: <$thrift>.HandlerSpec{
				<if .OneWay>
					Type: <$transport>.Oneway,
					Oneway: <import $onewayWrapperImport>.<$onewayWrapperFunc>(h.<.Name>),
				<else>
					Type: <$transport>.Unary,
					Unary: <import $unaryWrapperImport>.<$unaryWrapperFunc>(h.<.Name>),
				<end>
				},
				Signature: "<.Name>(<range $i, $v := .Arguments><if ne $i 0>, <end><.Name> <formatType .Type><end>)<if not .OneWay | and .ReturnType> (<formatType .ReturnType>)<end>",
				ThriftModule: <import $module.ImportPath>.ThriftModule,
				},
		<end>},
	}

	procedures := make([]<$transport>.Procedure, 0, <len .Functions>)
	<if .Parent> procedures = append(procedures, <import .ParentServerPackagePath>.New(impl, opts...)...)
	<end>         procedures = append(procedures, <$thrift>.BuildProcedures(service, opts...)...)
	return procedures
}

type handler struct{ impl Interface }

<$service := .>
<$module := .Module>
<range .Functions>
<$context := import $contextImportPath>
<$prefix := printf "%s.%s_%s_" (import $module.ImportPath) $service.Name .Name>

<$wire := import "go.uber.org/thriftrw/wire">

<if .OneWay>
func (h handler) <.Name>(ctx <$context>.Context, body <$wire>.Value) error {
	var args <$prefix>Args
	if err := args.FromWire(body); err != nil {
		return err
	}

	return h.impl.<.Name>(ctx, <range .Arguments>args.<.Name>,<end>)
}
<else>
func (h handler) <.Name>(ctx <$context>.Context, body <$wire>.Value) (<$thrift>.Response, error) {
	var args <$prefix>Args
	if err := args.FromWire(body); err != nil {
		return <$thrift>.Response{}, err
	}

	<if .ReturnType>
		success, err := h.impl.<.Name>(ctx, <range .Arguments>args.<.Name>,<end>)
	<else>
		err := h.impl.<.Name>(ctx, <range .Arguments>args.<.Name>,<end>)
	<end>

	hadError := err != nil
	result, err := <$prefix>Helper.WrapResponse(<if .ReturnType>success,<end> err)

	var response <$thrift>.Response
	if err == nil {
		response.IsApplicationError = hadError
		response.Body = result
	}
	return response, err
}
<end>
<end>
`

func serverGenerator(data *templateData, files map[string][]byte) (err error) {
	packageName := filepath.Base(data.ServerPackagePath())
	// kv.thrift => .../kv/keyvalueserver/server.go
	path := filepath.Join(data.Module.Directory, packageName, "server.go")
	files[path], err = plugin.GoFileFromTemplate(path, serverTemplate, data, templateOptions...)
	return
}

Is there a Go quoting operator for text templates?


@@ -153,6 +153,10 @@ func (agent *ecsAgent) appendFirelensFluentbitCapabilities(capabilities []*ecs.A
 	return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityFirelensFluentbit)
 }

+func (agent *ecsAgent) appendEFSCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
+	return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityEFS)
+}
+
 func (agent *ecsAgent) appendFirelensLoggingDriverCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
 	return appendNameOnlyAttribute(capabilities, capabilityPrefix+capabilityFirelensLoggingDriver)
 }

// +build linux

// Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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.

package app

import (
	"strings"

	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/dockerclient"
	"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi"
	"github.com/aws/amazon-ecs-agent/agent/ecs_client/model/ecs"
	"github.com/aws/amazon-ecs-agent/agent/ecscni"
	"github.com/aws/amazon-ecs-agent/agent/taskresource/volume"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/cihub/seelog"
)

const (
	AVX         = "avx"
	AVX2        = "avx2"
	SSE41       = "sse4_1"
	SSE42       = "sse4_2"
	CpuInfoPath = "/proc/cpuinfo"
)

func (agent *ecsAgent) appendVolumeDriverCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	// "local" is default docker driver
	capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilityDockerPluginInfix+volume.DockerLocalVolumeDriver)

	// for non-standardized plugins, call docker pkg's plugins.Scan()
	nonStandardizedPlugins, err := agent.mobyPlugins.Scan()
	if err != nil {
		seelog.Warnf("Scanning plugins failed: %v", err)
		// do not return yet, we need the list of plugins below. range handles nil slice.
	}

	for _, pluginName := range nonStandardizedPlugins {
		// Replace the ':' to '.' in the plugin name for attributes
		capabilities = appendNameOnlyAttribute(capabilities,
			attributePrefix+capabilityDockerPluginInfix+strings.Replace(pluginName, config.DockerTagSeparator, attributeSeparator, -1))
	}

	// for standardized plugins, call docker's plugin ls API
	pluginEnabled := true
	volumeDriverType := []string{dockerapi.VolumeDriverType}
	standardizedPlugins, err := agent.dockerClient.ListPluginsWithFilters(agent.ctx, pluginEnabled, volumeDriverType, dockerclient.ListPluginsTimeout)
	if err != nil {
		seelog.Warnf("Listing plugins with filters enabled=%t, capabilities=%v failed: %v", pluginEnabled, volumeDriverType, err)
		return capabilities
	}

	// For plugin with default tag latest, register two attributes with and without the latest tag
	// as the tag is optional and can be added by docker or customer
	for _, pluginName := range standardizedPlugins {
		names := strings.Split(pluginName, config.DockerTagSeparator)
		if len(names) > 1 && names[len(names)-1] == config.DefaultDockerTag {
			capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilityDockerPluginInfix+strings.Join(names[:len(names)-1], attributeSeparator))
		}

		capabilities = appendNameOnlyAttribute(capabilities,
			attributePrefix+capabilityDockerPluginInfix+strings.Replace(pluginName, config.DockerTagSeparator, attributeSeparator, -1))
	}
	return capabilities
}

func (agent *ecsAgent) appendNvidiaDriverVersionAttribute(capabilities []*ecs.Attribute) []*ecs.Attribute {
	if agent.resourceFields != nil && agent.resourceFields.NvidiaGPUManager != nil {
		driverVersion := agent.resourceFields.NvidiaGPUManager.GetDriverVersion()
		if driverVersion != "" {
			capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilityNvidiaDriverVersionInfix+driverVersion)
		}
	}
	return capabilities
}

func (agent *ecsAgent) appendENITrunkingCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	if !agent.cfg.ENITrunkingEnabled {
		return capabilities
	}
	capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+taskENITrunkingAttributeSuffix)
	return agent.appendBranchENIPluginVersionAttribute(capabilities)
}

func (agent *ecsAgent) appendBranchENIPluginVersionAttribute(capabilities []*ecs.Attribute) []*ecs.Attribute {
	version, err := agent.cniClient.Version(ecscni.ECSBranchENIPluginName)
	if err != nil {
		seelog.Warnf(
			"Unable to determine the version of the plugin '%s': %v",
			ecscni.ECSBranchENIPluginName, err)
		return capabilities
	}

	return append(capabilities, &ecs.Attribute{
		Name:  aws.String(attributePrefix + branchCNIPluginVersionSuffix),
		Value: aws.String(version),
	})
}

func (agent *ecsAgent) appendPIDAndIPCNamespaceSharingCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	return appendNameOnlyAttribute(capabilities, attributePrefix+capabiltyPIDAndIPCNamespaceSharing)
}

func (agent *ecsAgent) appendAppMeshCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	return appendNameOnlyAttribute(capabilities, attributePrefix+appMeshAttributeSuffix)
}

func (agent *ecsAgent) appendTaskEIACapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {

	capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+taskEIAAttributeSuffix)

	eiaRequiredFlags := []string{AVX, AVX2, SSE41, SSE42}
	cpuInfo, err := utils.ReadCPUInfo(CpuInfoPath)
	if err != nil {
		seelog.Warnf("Unable to read cpuinfo: %v", err)
		return capabilities
	}

	flagMap := utils.GetCPUFlags(cpuInfo)
	missingFlags := []string{}
	for _, requiredFlag := range eiaRequiredFlags {
		if _, ok := flagMap[requiredFlag]; !ok {
			missingFlags = append(missingFlags, requiredFlag)
		}
	}

	if len(missingFlags) > 0 {
		seelog.Infof("Missing cpu flags for EIA support: %v", strings.Join(missingFlags, ","))
		return capabilities
	}

	return appendNameOnlyAttribute(capabilities, attributePrefix+taskEIAWithOptimizedCPU)
}

func (agent *ecsAgent) appendFirelensFluentdCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityFirelensFluentd)
}

func (agent *ecsAgent) appendFirelensFluentbitCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityFirelensFluentbit)
}

func (agent *ecsAgent) appendFirelensLoggingDriverCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	return appendNameOnlyAttribute(capabilities, capabilityPrefix+capabilityFirelensLoggingDriver)
}

func (agent *ecsAgent) appendFirelensConfigCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute {
	capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilityFirelensConfigFile)
	return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityFirelensConfigS3)
}


@@ -1,9 +1,25 @@
+/*
+Copyright 2019 The KubeEdge Authors.
+
+Licensed 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.
+*/
 package status

 import (
-	edgeapi "github.com/kubeedge/kubeedge/common/types"
 	"time"

+	edgeapi "github.com/kubeedge/kubeedge/common/types"
+
 	"github.com/kubeedge/beehive/pkg/common/log"
 	"github.com/kubeedge/kubeedge/edge/pkg/edged/podmanager"
 	"github.com/kubeedge/kubeedge/edge/pkg/metamanager/client"

package status

import (
	edgeapi "github.com/kubeedge/kubeedge/common/types"
	"time"

	"github.com/kubeedge/beehive/pkg/common/log"
	"github.com/kubeedge/kubeedge/edge/pkg/edged/podmanager"
	"github.com/kubeedge/kubeedge/edge/pkg/metamanager/client"
	"k8s.io/api/core/v1"
	apiequality "k8s.io/apimachinery/pkg/api/equality"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/pkg/kubelet/status"
)

// manager as status manager, embedded a k8s.io/kubernetes/pkg/kubelet/status.Manager
// inherit it's method but refactored Start() function to periodicity update status to IEF
type manager struct {
	status.Manager
	// TODO: consider need lock?
	podManager        podmanager.Manager
	apiStatusVersions map[types.UID]*v1.PodStatus
	metaClient        client.CoreInterface
}

//NewManager creates and returns a new manager object
func NewManager(kubeClient clientset.Interface, podManager podmanager.Manager, podDeletionSafety status.PodDeletionSafetyProvider, metaClient client.CoreInterface) status.Manager {
	kubeManager := status.NewManager(kubeClient, podManager, podDeletionSafety)
	return &manager{
		Manager:           kubeManager,
		metaClient:        metaClient,
		podManager:        podManager,
		apiStatusVersions: make(map[types.UID]*v1.PodStatus),
	}
}

const syncPeriod = 10 * time.Second

func (m *manager) Start() {
	log.LOGGER.Info("Starting to sync pod status with apiserver")
	syncTicker := time.Tick(syncPeriod)

	go wait.Forever(func() {
		select {
		case <-syncTicker:
			m.updatePodStatus()
		}
	}, 0)
}

func (m *manager) updatePodStatus() {
	for _, pod := range m.podManager.GetPods() {
		uid := pod.UID
		podStatus, ok := m.GetPodStatus(uid)
		if !ok || &podStatus == nil {
			continue
		}
		latestStatus, ok := m.apiStatusVersions[uid]
		if ok && apiequality.Semantic.DeepEqual(latestStatus, &podStatus) {
			continue
		}
		s := *podStatus.DeepCopy()
		var conditionFlag bool
		podCondition := v1.PodCondition{Type: v1.PodReady, Status: v1.ConditionFalse, Reason: "ContainersNotReady"}
		for idx, cs := range podStatus.ContainerStatuses {
			if cs.State.Running != nil && cs.State.Running.StartedAt.Unix() == 0 {
				newState := v1.ContainerState{Waiting: &v1.ContainerStateWaiting{
					Reason:  "CrashLoopBackOff",
					Message: "Container restarting in container runtime",
				}}
				s.ContainerStatuses[idx].State = newState
				conditionFlag = true
			}
		}
		var podReadyFlag bool
		if conditionFlag {
			if s.Conditions == nil {
				s.Conditions = append(s.Conditions, podCondition)
			} else {
				for index, condition := range s.Conditions {
					if condition.Type == v1.PodReady {
						s.Conditions[index].Status = v1.ConditionFalse
						s.Conditions[index].Reason = "ContainersNotReady"
						podReadyFlag = true
						break
					}
				}
				if !podReadyFlag {
					s.Conditions = append(s.Conditions, podCondition)
				}
			}
		}

		err := m.metaClient.PodStatus(pod.Namespace).Update(pod.Name, edgeapi.PodStatusRequest{UID: pod.UID, Name: pod.Name, Status: s})
		if err != nil {
			log.LOGGER.Errorf("Update pod status failed err :%v", err)
		}
		log.LOGGER.Infof("Status for pod %s updated successfully: %+v", pod.Name, podStatus)
		m.apiStatusVersions[pod.UID] = podStatus.DeepCopy()
	}
}


@@ -48,7 +48,7 @@ func TestBroadcast(t *testing.T) {
 		}
 	}
 	u := func(_ context.Context, _ uint32, _ peerstore.PeerInfo, _ proto.Message) {}
-	bootnodePort := testutil.RandomPort()
+	bootnodePort := 14689
 	cfg := config.Config{
 		Network: config.Network{Host: "127.0.0.1", Port: bootnodePort},
 	}

// Copyright (c) 2019 IoTeX Foundation
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.

package p2p

import (
	"context"
	"net"
	"sync"
	"testing"
	"time"

	"github.com/golang/protobuf/proto"
	peerstore "github.com/libp2p/go-libp2p-peerstore"
	"github.com/stretchr/testify/require"

	"github.com/iotexproject/iotex-core/config"
	"github.com/iotexproject/iotex-core/testutil"
	"github.com/iotexproject/iotex-proto/golang/testingpb"
)

func TestBroadcast(t *testing.T) {
	ctx := context.Background()
	n := 10
	agents := make([]*Agent, 0)
	defer func() {
		var err error
		for _, agent := range agents {
			err = agent.Stop(ctx)
		}
		require.NoError(t, err)
	}()
	counts := make(map[uint8]int)
	var mutex sync.RWMutex
	b := func(_ context.Context, _ uint32, msg proto.Message) {
		mutex.Lock()
		defer mutex.Unlock()
		testMsg, ok := msg.(*testingpb.TestPayload)
		require.True(t, ok)
		idx := testMsg.MsgBody[0]
		if _, ok = counts[idx]; ok {
			counts[idx]++
		} else {
			counts[idx] = 1
		}
	}
	u := func(_ context.Context, _ uint32, _ peerstore.PeerInfo, _ proto.Message) {}
	bootnodePort := testutil.RandomPort()
	cfg := config.Config{
		Network: config.Network{Host: "127.0.0.1", Port: bootnodePort},
	}
	bootnode := NewAgent(cfg, b, u)
	require.NoError(t, bootnode.Start(ctx))
	require.NoError(t, testutil.WaitUntil(100*time.Millisecond, 10*time.Second, func() (b bool, e error) {
		ip := net.ParseIP("127.0.0.1")
		tcpAddr := net.TCPAddr{
			IP:   ip,
			Port: bootnodePort,
		}
		_, err := net.DialTCP("tcp", nil, &tcpAddr)
		return err == nil, nil
	}))
	for i := 0; i < n; i++ {
		port := testutil.RandomPort()
		cfg := config.Config{
			Network: config.Network{
				Host:           "127.0.0.1",
				Port:           port,
				BootstrapNodes: []string{bootnode.Self()[0].String()},
			},
		}
		agent := NewAgent(cfg, b, u)
		require.NoError(t, agent.Start(ctx))
		require.NoError(t, testutil.WaitUntil(100*time.Millisecond, 10*time.Second, func() (b bool, e error) {
			ip := net.ParseIP("127.0.0.1")
			tcpAddr := net.TCPAddr{
				IP:   ip,
				Port: port,
			}
			_, err := net.DialTCP("tcp", nil, &tcpAddr)
			return err == nil, nil
		}))
		agents = append(agents, agent)
	}

	for i := 0; i < n; i++ {
		require.NoError(t, agents[i].BroadcastOutbound(WitContext(ctx, Context{ChainID: 1}), &testingpb.TestPayload{
			MsgBody: []byte{uint8(i)},
		}))
		require.NoError(t, testutil.WaitUntil(100*time.Millisecond, 20*time.Second, func() (bool, error) {
			mutex.RLock()
			defer mutex.RUnlock()
			// Broadcast message will be skipped by the source node
			return counts[uint8(i)] == n, nil
		}))
	}
}

func TestUnicast(t *testing.T) {
	ctx := context.Background()
	n := 10
	agents := make([]*Agent, 0)
	defer func() {
		var err error
		for _, agent := range agents {
			err = agent.Stop(ctx)
		}
		require.NoError(t, err)
	}()
	counts := make(map[uint8]int)
	var src string
	var mutex sync.RWMutex
	b := func(_ context.Context, _ uint32, _ proto.Message) {}
	u := func(_ context.Context, _ uint32, peer peerstore.PeerInfo, msg proto.Message) {
		mutex.Lock()
		defer mutex.Unlock()
		testMsg, ok := msg.(*testingpb.TestPayload)
		require.True(t, ok)
		idx := testMsg.MsgBody[0]
		if _, ok = counts[idx]; ok {
			counts[idx]++
		} else {
			counts[idx] = 1
		}
		src = peer.ID.Pretty()
	}

	bootnode := NewAgent(config.Config{
		Network: config.Network{Host: "127.0.0.1", Port: testutil.RandomPort()},
	}, b, u)
	require.NoError(t, bootnode.Start(ctx))

	for i := 0; i < n; i++ {
		cfg := config.Config{
			Network: config.Network{
				Host:           "127.0.0.1",
				Port:           testutil.RandomPort(),
				BootstrapNodes: []string{bootnode.Self()[0].String()},
			},
		}
		agent := NewAgent(cfg, b, u)
		require.NoError(t, agent.Start(ctx))
		agents = append(agents, agent)
	}

	for i := 0; i < n; i++ {
		neighbors, err := agents[i].Neighbors(ctx)
		require.NoError(t, err)
		require.True(t, len(neighbors) > 0)
		for _, neighbor := range neighbors {
			require.NoError(t, agents[i].UnicastOutbound(WitContext(ctx, Context{ChainID: 1}), neighbor, &testingpb.TestPayload{
				MsgBody: []byte{uint8(i)},
			}))
		}
		require.NoError(t, testutil.WaitUntil(100*time.Millisecond, 20*time.Second, func() (bool, error) {
			mutex.RLock()
			defer mutex.RUnlock()
			return counts[uint8(i)] == len(neighbors) && src == agents[i].Info().ID.Pretty(), nil
		}))
	}
}

Can we still random, but if we randomly get a port is used before, we randomly pick again?


@@ -41,11 +41,19 @@ func (w *watcher) ContainerStarted(id string) {
 			continue
 		}
 		if network.isOurs {
-			fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
-			if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
-				w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
+			if !w.driver.noDNS {
+				fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
+				if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
+					w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
+				}
 			}
-			if _, err := weavenet.WithNetNSByPid(info.State.Pid, "configure-arp", weavenet.VethName); err != nil {
+			rootDir := "/"
+			if w.driver.isPluginV2 {
+				// We bind mount host's /proc to /host/proc for plugin-v2
+				rootDir = "/host"
+			}
+			netNSPath := weavenet.NSPathByPidWithRoot(rootDir, info.State.Pid)
+			if _, err := weavenet.WithNetNS(netNSPath, "configure-arp", weavenet.VethName); err != nil {
 				w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
 			}
 		}

package plugin

import (
	"fmt"

	weaveapi "github.com/weaveworks/weave/api"
	"github.com/weaveworks/weave/common/docker"
	weavenet "github.com/weaveworks/weave/net"
)

const (
	WeaveDomain = "weave.local"
)

type watcher struct {
	client *docker.Client
	weave  *weaveapi.Client
	driver *driver
}

type Watcher interface {
}

func NewWatcher(client *docker.Client, weave *weaveapi.Client, driver *driver) (Watcher, error) {
	w := &watcher{client: client, weave: weave, driver: driver}
	return w, client.AddObserver(w)
}

func (w *watcher) ContainerStarted(id string) {
	w.driver.debug("ContainerStarted", "%s", id)
	info, err := w.client.InspectContainer(id)
	if err != nil {
		w.driver.warn("ContainerStarted", "error inspecting container %s: %s", id, err)
		return
	}
	// check that it's on our network
	for _, net := range info.NetworkSettings.Networks {
		network, err := w.driver.findNetworkInfo(net.NetworkID)
		if err != nil {
			w.driver.warn("ContainerStarted", "unable to find network %s info: %s", net.NetworkID, err)
			continue
		}
		if network.isOurs {
			fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
			if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
				w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
			}
			if _, err := weavenet.WithNetNSByPid(info.State.Pid, "configure-arp", weavenet.VethName); err != nil {
				w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
			}
		}
	}
}

func (w *watcher) ContainerDied(id string) {
	// don't need to do this as WeaveDNS removes names on container died anyway
	// (note by the time we get this event we can't see the EndpointID)
}

func (w *watcher) ContainerDestroyed(id string) {}

*Minor*: double negation. Suggestion: `dnsEnabled bool` or even just `dns bool` instead of `noDNS bool`.


@@ -59,10 +59,11 @@ func (client *clientRest) NodeRegister(proposal dto_discovery.ServiceProposal) (
 	return
 }

-func (client *clientRest) NodeSendStats(nodeKey string, sessionList []dto.SessionStatsDeprecated) (err error) {
+func (client *clientRest) NodeSendStats(nodeKey string) (err error) {
 	response, err := client.doPostRequest("node_send_stats", dto.NodeStatsRequest{
-		NodeKey:  nodeKey,
-		Sessions: sessionList,
+		NodeKey: nodeKey,
+		// TODO: remove this struct in favor of `SessionStats`
+		Sessions: []dto.SessionStats{},
 	})
 	if err == nil {
 		defer response.Body.Close()

package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/cihub/seelog"
	"github.com/mysterium/node/identity"
	"github.com/mysterium/node/server/dto"
	dto_discovery "github.com/mysterium/node/service_discovery/dto"
	"net/url"
)

var mysteriumApiUrl string

const MYSTERIUM_API_CLIENT = "goclient-v0.1"
const MYSTERIUM_API_LOG_PREFIX = "[Mysterium.api] "

func NewClient() Client {
	httpClient := http.Client{
		Transport: &http.Transport{},
	}
	return &clientRest{
		httpClient: httpClient,
	}
}

type clientRest struct {
	httpClient http.Client
}

func (client *clientRest) RegisterIdentity(identity identity.Identity) (err error) {
	response, err := client.doPostRequest("identities", dto.CreateIdentityRequest{
		Identity: identity.Address,
	})

	if err == nil {
		defer response.Body.Close()
		log.Info(MYSTERIUM_API_LOG_PREFIX, "Identity registered: ", identity)
	}

	return
}

func (client *clientRest) NodeRegister(proposal dto_discovery.ServiceProposal) (err error) {
	response, err := client.doPostRequest("node_register", dto.NodeRegisterRequest{
		ServiceProposal: proposal,
	})

	if err == nil {
		defer response.Body.Close()
		log.Info(MYSTERIUM_API_LOG_PREFIX, "Node registered: ", proposal.ProviderId)
	}

	return
}

func (client *clientRest) NodeSendStats(nodeKey string, sessionList []dto.SessionStatsDeprecated) (err error) {
	response, err := client.doPostRequest("node_send_stats", dto.NodeStatsRequest{
		NodeKey:  nodeKey,
		Sessions: sessionList,
	})
	if err == nil {
		defer response.Body.Close()
		log.Info(MYSTERIUM_API_LOG_PREFIX, "Node stats sent: ", nodeKey)
	}

	return nil
}

func (client *clientRest) FindProposals(nodeKey string) (proposals []dto_discovery.ServiceProposal, err error) {
	values := url.Values{}
	values.Set("node_key", nodeKey)
	response, err := client.doGetRequest("proposals", values)

	if err != nil {
		return
	}

	defer response.Body.Close()

	var proposalsResponse dto.ProposalsResponse
	err = parseResponseJson(response, &proposalsResponse)
	if err != nil {
		return
	}
	proposals = proposalsResponse.Proposals

	log.Info(MYSTERIUM_API_LOG_PREFIX, "FindProposals fetched: ", proposals)

	return
}

func (client *clientRest) SendSessionStats(sessionId string, sessionStats dto.SessionStats) (err error) {
	path := fmt.Sprintf("sessions/%s/stats", sessionId)
	response, err := client.doPostRequest(path, sessionStats)
	if err == nil {
		defer response.Body.Close()
		log.Info(MYSTERIUM_API_LOG_PREFIX, "Session stats sent: ", sessionId)
	}

	return nil
}

func (client *clientRest) doGetRequest(path string, values url.Values) (*http.Response, error) {
	fullPath := fmt.Sprintf("%v/%v?%v", mysteriumApiUrl, path, values.Encode())
	return client.executeRequest("GET", fullPath, nil)
}

func (client *clientRest) doPostRequest(path string, payload interface{}) (*http.Response, error) {
	return client.doPayloadRequest("POST", path, payload)
}

func (client *clientRest) doPayloadRequest(method, path string, payload interface{}) (*http.Response, error) {
	payloadJson, err := json.Marshal(payload)
	if err != nil {
		log.Critical(MYSTERIUM_API_LOG_PREFIX, err)
		return nil, err
	}

	return client.executeRequest(method, mysteriumApiUrl+"/"+path, payloadJson)
}

func (client *clientRest) executeRequest(method, fullPath string, payloadJson []byte) (*http.Response, error) {
	request, err := http.NewRequest(method, fullPath, bytes.NewBuffer(payloadJson))
	request.Header.Set("User-Agent", MYSTERIUM_API_CLIENT)
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("Accept", "application/json")
	if err != nil {
		log.Critical(MYSTERIUM_API_LOG_PREFIX, err)
		return nil, err
	}

	response, err := client.httpClient.Do(request)

	if err != nil {
		log.Error(MYSTERIUM_API_LOG_PREFIX, err)
		return response, err
	}

	err = parseResponseError(response)
	if err != nil {
		log.Error(MYSTERIUM_API_LOG_PREFIX, err)
		return response, err
	}

	return response, nil
}

func parseResponseJson(response *http.Response, dto interface{}) error {
	responseJson, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(responseJson, dto)
	if err != nil {
		return err
	}

	return nil
}

func parseResponseError(response *http.Response) error {
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		return errors.New(fmt.Sprintf("Server response invalid: %s (%s)", response.Status, response.Request.URL))
	}

	return nil
}


@@ -3,28 +3,35 @@ package ipam
 import (
 	"fmt"

+	"github.com/weaveworks/weave/common"
 	"github.com/weaveworks/weave/ipam/address"
 	"github.com/weaveworks/weave/router"
 )

 type claim struct {
-	resultChan       chan<- error
-	ident            string
-	addr             address.Address
-	hasBeenCancelled func() bool
+	resultChan chan<- error
+	ident      string
+	addr       address.Address
 }

-// Try returns true for success (or failure), false if we need to try again later
-func (c *claim) Try(alloc *Allocator) bool {
-	if (c.hasBeenCancelled)() {
-		c.Cancel()
-		return true
+func (c *claim) sendResult(result error) {
+	// Make sure we only send a result once, since listener stops listening after that
+	if c.resultChan != nil {
+		c.resultChan <- result
+		close(c.resultChan)
+		c.resultChan = nil
+	}
+	if result != nil {
+		common.Error.Println("[allocator] " + result.Error())
 	}
+}

+// Try returns true for success (or failure), false if we need to try again later
+func (c *claim) Try(alloc *Allocator) bool {
 	if !alloc.ring.Contains(c.addr) {
 		// Address not within our universe; assume user knows what they are doing
 		alloc.infof("Ignored address %s claimed by %s - not in our universe\n", c.addr, c.ident)
-		c.resultChan <- nil
+		c.sendResult(nil)
 		return true
 	}

package ipam

import (
	"fmt"

	"github.com/weaveworks/weave/ipam/address"
	"github.com/weaveworks/weave/router"
)

type claim struct {
	resultChan       chan<- error
	ident            string
	addr             address.Address
	hasBeenCancelled func() bool
}

// Try returns true for success (or failure), false if we need to try again later
func (c *claim) Try(alloc *Allocator) bool {
	if (c.hasBeenCancelled)() {
		c.Cancel()
		return true
	}

	if !alloc.ring.Contains(c.addr) {
		// Address not within our universe; assume user knows what they are doing
		alloc.infof("Ignored address %s claimed by %s - not in our universe\n", c.addr, c.ident)
		c.resultChan <- nil
		return true
	}

	// If our ring doesn't know, it must be empty.  We will have initiated the
	// bootstrap of the ring, so wait until we find some owner for this
	// range (might be us).
	owner := alloc.ring.Owner(c.addr)
	if owner == router.UnknownPeerName {
		alloc.infof("Ring is empty; will try later.\n", c.addr, owner)
		return false
	}
	if owner != alloc.ourName {
		name, found := alloc.nicknames[owner]
		if found {
			name = " (" + name + ")"
		}
		c.resultChan <- fmt.Errorf("address %s is owned by other peer %s%s", c.addr.String(), owner, name)
		return true
	}
	// We are the owner, check we haven't given it to another container
	existingIdent := alloc.findOwner(c.addr)
	if existingIdent == c.ident {
		// same identifier is claiming same address; that's OK
		c.resultChan <- nil
		return true
	}
	if existingIdent == "" {
		err := alloc.space.Claim(c.addr)
		if err != nil {
			c.resultChan <- err
			return true
		}
		alloc.debugln("Claimed", c.addr, "for", c.ident)
		alloc.addOwned(c.ident, c.addr)
		c.resultChan <- nil
		return true
	}
	// Addr already owned by container on this machine
	c.resultChan <- fmt.Errorf("address %s is already owned by %s", c.addr.String(), existingIdent)
	return true
}

func (c *claim) Cancel() {
	c.resultChan <- fmt.Errorf("Operation cancelled.")
}

func (c *claim) String() string {
	return fmt.Sprintf("Claim %s -> %s", c.ident, c.addr.String())
}

func (c *claim) ForContainer(ident string) bool {
	return c.ident == ident
}

This error could do with a little more context. "Delayed claim failed for %s: %v" or such like.