text:
package simulation

import (
	"fmt"
	"math/rand"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/irisnet/irishub/baseapp"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/x/auth"
	"github.com/irisnet/irishub/simulation/mock"
	"github.com/irisnet/irishub/simulation/mock/simulation"
	"github.com/cosmos/cosmos-sdk/x/stake"
	abci "github.com/tendermint/tendermint/abci/types"
	"github.com/tendermint/tendermint/crypto"
)

// SimulateMsgCreateValidator
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		denom := k.GetParams(ctx).BondDenom
		description := stake.Description{
			Moniker: simulation.RandStringOfLength(r, 10),
		}
		key := simulation.RandomKey(r, keys)
		pubkey := key.PubKey()
		address := sdk.AccAddress(pubkey.Address())
		amount := m.GetAccount(ctx, address).GetCoins().AmountOf(denom)
		if amount.GT(sdk.ZeroInt()) {
			amount = simulation.RandomAmount(r, amount)
		}
		if amount.Equal(sdk.ZeroInt()) {
			return "no-operation", nil
		}
		msg := stake.MsgCreateValidator{
			Description:   description,
			ValidatorAddr: address,
			DelegatorAddr: address,
			PubKey:        pubkey,
			Delegation:    sdk.NewCoin(denom, amount),
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgCreateValidator/%v", result.IsOK()))
		// require.True(t, result.IsOK(), "expected OK result but instead got %v", result)
		action = fmt.Sprintf("TestMsgCreateValidator: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
		return action, nil
	}
}

// SimulateMsgEditValidator
func SimulateMsgEditValidator(k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		description := stake.Description{
			Moniker:  simulation.RandStringOfLength(r, 10),
			Identity: simulation.RandStringOfLength(r, 10),
			Website:  simulation.RandStringOfLength(r, 10),
			Details:  simulation.RandStringOfLength(r, 10),
		}
		key := simulation.RandomKey(r, keys)
		pubkey := key.PubKey()
		address := sdk.AccAddress(pubkey.Address())
		msg := stake.MsgEditValidator{
			Description:   description,
			ValidatorAddr: address,
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgEditValidator/%v", result.IsOK()))
		action = fmt.Sprintf("TestMsgEditValidator: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
		return action, nil
	}
}

// SimulateMsgDelegate
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		denom := k.GetParams(ctx).BondDenom
		validatorKey := simulation.RandomKey(r, keys)
		validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
		delegatorKey := simulation.RandomKey(r, keys)
		delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
		amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
		if amount.GT(sdk.ZeroInt()) {
			amount = simulation.RandomAmount(r, amount)
		}
		if amount.Equal(sdk.ZeroInt()) {
			return "no-operation", nil
		}
		msg := stake.MsgDelegate{
			DelegatorAddr: delegatorAddress,
			ValidatorAddr: validatorAddress,
			Delegation:    sdk.NewCoin(denom, amount),
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgDelegate/%v", result.IsOK()))
		action = fmt.Sprintf("TestMsgDelegate: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
		return action, nil
	}
}

// SimulateMsgBeginUnbonding
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		denom := k.GetParams(ctx).BondDenom
		validatorKey := simulation.RandomKey(r, keys)
		validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
		delegatorKey := simulation.RandomKey(r, keys)
		delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
		amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
		if amount.GT(sdk.ZeroInt()) {
			amount = simulation.RandomAmount(r, amount)
		}
		if amount.Equal(sdk.ZeroInt()) {
			return "no-operation", nil
		}
		msg := stake.MsgBeginUnbonding{
			DelegatorAddr: delegatorAddress,
			ValidatorAddr: validatorAddress,
			SharesAmount:  sdk.NewRatFromInt(amount),
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgBeginUnbonding/%v", result.IsOK()))
		action = fmt.Sprintf("TestMsgBeginUnbonding: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
		return action, nil
	}
}

// SimulateMsgCompleteUnbonding
func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		validatorKey := simulation.RandomKey(r, keys)
		validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
		delegatorKey := simulation.RandomKey(r, keys)
		delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
		msg := stake.MsgCompleteUnbonding{
			DelegatorAddr: delegatorAddress,
			ValidatorAddr: validatorAddress,
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgCompleteUnbonding/%v", result.IsOK()))
		action = fmt.Sprintf("TestMsgCompleteUnbonding: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
		return action, nil
	}
}

// SimulateMsgBeginRedelegate
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		denom := k.GetParams(ctx).BondDenom
		sourceValidatorKey := simulation.RandomKey(r, keys)
		sourceValidatorAddress := sdk.AccAddress(sourceValidatorKey.PubKey().Address())
		destValidatorKey := simulation.RandomKey(r, keys)
		destValidatorAddress := sdk.AccAddress(destValidatorKey.PubKey().Address())
		delegatorKey := simulation.RandomKey(r, keys)
		delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
		// TODO
		amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
		if amount.GT(sdk.ZeroInt()) {
			amount = simulation.RandomAmount(r, amount)
		}
		if amount.Equal(sdk.ZeroInt()) {
			return "no-operation", nil
		}
		msg := stake.MsgBeginRedelegate{
			DelegatorAddr:    delegatorAddress,
			ValidatorSrcAddr: sourceValidatorAddress,
			ValidatorDstAddr: destValidatorAddress,
			SharesAmount:     sdk.NewRatFromInt(amount),
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgBeginRedelegate/%v", result.IsOK()))
		action = fmt.Sprintf("TestMsgBeginRedelegate: %s", msg.GetSignBytes())
		return action, nil
	}
}

// SimulateMsgCompleteRedelegate
func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.TestAndRunTx {
	return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) {
		validatorSrcKey := simulation.RandomKey(r, keys)
		validatorSrcAddress := sdk.AccAddress(validatorSrcKey.PubKey().Address())
		validatorDstKey := simulation.RandomKey(r, keys)
		validatorDstAddress := sdk.AccAddress(validatorDstKey.PubKey().Address())
		delegatorKey := simulation.RandomKey(r, keys)
		delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
		msg := stake.MsgCompleteRedelegate{
			DelegatorAddr:    delegatorAddress,
			ValidatorSrcAddr: validatorSrcAddress,
			ValidatorDstAddr: validatorDstAddress,
		}
		require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
		ctx, write := ctx.CacheContext()
		result := stake.NewHandler(k)(ctx, msg)
		if result.IsOK() {
			write()
		}
		event(fmt.Sprintf("stake/MsgCompleteRedelegate/%v", result.IsOK()))
		action = fmt.Sprintf("TestMsgCompleteRedelegate: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
		return action, nil
	}
}

// Setup
func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {
	return func(r *rand.Rand, privKeys []crypto.PrivKey) {
		ctx := mapp.NewContext(false, abci.Header{})
		gen := stake.DefaultGenesisState()
		gen.Params.InflationMax = sdk.NewRat(0)
		gen.Params.InflationMin = sdk.NewRat(0)
		stake.InitGenesis(ctx, k, gen)
		params := k.GetParams(ctx)
		denom := params.BondDenom
		loose := sdk.ZeroInt()
		mapp.AccountMapper.IterateAccounts(ctx, func(acc auth.Account) bool {
			balance := simulation.RandomAmount(r, sdk.NewInt(1000000))
			acc.SetCoins(acc.GetCoins().Plus(sdk.Coins{sdk.NewCoin(denom, balance)}))
			mapp.AccountMapper.SetAccount(ctx, acc)
			loose = loose.Add(balance)
			return false
		})
		pool := k.GetPool(ctx)
		pool.LooseTokens = pool.LooseTokens.Add(sdk.NewRat(loose.Int64(), 1))
		k.SetPool(ctx, pool)
	}
}

repository: leezhihui/irishub

repo_name: leezhihui/irishub


text:
/*
 *
 * Copyright 2021 gRPC 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 xdsresource

import (
	"fmt"
	"math"
	"regexp"
	"strings"
	"time"

	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/internal/envconfig"
	"google.golang.org/grpc/internal/xds/matcher"
	"google.golang.org/grpc/xds/internal/clusterspecifier"
	"google.golang.org/protobuf/types/known/anypb"

	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
	v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3"
)

func unmarshalRouteConfigResource(r *anypb.Any) (string, RouteConfigUpdate, error) {
	r, err := UnwrapResource(r)
	if err != nil {
		return "", RouteConfigUpdate{}, fmt.Errorf("failed to unwrap resource: %v", err)
	}

	if !IsRouteConfigResource(r.GetTypeUrl()) {
		return "", RouteConfigUpdate{}, fmt.Errorf("unexpected resource type: %q ", r.GetTypeUrl())
	}
	rc := &v3routepb.RouteConfiguration{}
	if err := proto.Unmarshal(r.GetValue(), rc); err != nil {
		return "", RouteConfigUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
	}

	u, err := generateRDSUpdateFromRouteConfiguration(rc)
	if err != nil {
		return rc.GetName(), RouteConfigUpdate{}, err
	}
	u.Raw = r
	return rc.GetName(), u, nil
}

// generateRDSUpdateFromRouteConfiguration checks if the provided
// RouteConfiguration meets the expected criteria. If so, it returns a
// RouteConfigUpdate with nil error.
//
// A RouteConfiguration resource is considered valid when only if it contains a
// VirtualHost whose domain field matches the server name from the URI passed
// to the gRPC channel, and it contains a clusterName or a weighted cluster.
//
// The RouteConfiguration includes a list of virtualHosts, which may have zero
// or more elements. We are interested in the element whose domains field
// matches the server name specified in the "xds:" URI. The only field in the
// VirtualHost proto that the we are interested in is the list of routes. We
// only look at the last route in the list (the default route), whose match
// field must be empty and whose route field must be set.  Inside that route
// message, the cluster field will contain the clusterName or weighted clusters
// we are looking for.
func generateRDSUpdateFromRouteConfiguration(rc *v3routepb.RouteConfiguration) (RouteConfigUpdate, error) {
	vhs := make([]*VirtualHost, 0, len(rc.GetVirtualHosts()))
	csps := make(map[string]clusterspecifier.BalancerConfig)
	if envconfig.XDSRLS {
		var err error
		csps, err = processClusterSpecifierPlugins(rc.ClusterSpecifierPlugins)
		if err != nil {
			return RouteConfigUpdate{}, fmt.Errorf("received route is invalid: %v", err)
		}
	}
	// cspNames represents all the cluster specifiers referenced by Route
	// Actions - any cluster specifiers not referenced by a Route Action can be
	// ignored and not emitted by the xdsclient.
	var cspNames = make(map[string]bool)
	for _, vh := range rc.GetVirtualHosts() {
		routes, cspNs, err := routesProtoToSlice(vh.Routes, csps)
		if err != nil {
			return RouteConfigUpdate{}, fmt.Errorf("received route is invalid: %v", err)
		}
		for n := range cspNs {
			cspNames[n] = true
		}
		rc, err := generateRetryConfig(vh.GetRetryPolicy())
		if err != nil {
			return RouteConfigUpdate{}, fmt.Errorf("received route is invalid: %v", err)
		}
		vhOut := &VirtualHost{
			Domains:     vh.GetDomains(),
			Routes:      routes,
			RetryConfig: rc,
		}
		cfgs, err := processHTTPFilterOverrides(vh.GetTypedPerFilterConfig())
		if err != nil {
			return RouteConfigUpdate{}, fmt.Errorf("virtual host %+v: %v", vh, err)
		}
		vhOut.HTTPFilterConfigOverride = cfgs
		vhs = append(vhs, vhOut)
	}

	// "For any entry in the RouteConfiguration.cluster_specifier_plugins not
	// referenced by an enclosed ActionType's cluster_specifier_plugin, the xDS
	// client should not provide it to its consumers." - RLS in xDS Design
	for name := range csps {
		if !cspNames[name] {
			delete(csps, name)
		}
	}

	return RouteConfigUpdate{VirtualHosts: vhs, ClusterSpecifierPlugins: csps}, nil
}

func processClusterSpecifierPlugins(csps []*v3routepb.ClusterSpecifierPlugin) (map[string]clusterspecifier.BalancerConfig, error) {
	cspCfgs := make(map[string]clusterspecifier.BalancerConfig)
	// "The xDS client will inspect all elements of the
	// cluster_specifier_plugins field looking up a plugin based on the
	// extension.typed_config of each." - RLS in xDS design
	for _, csp := range csps {
		cs := clusterspecifier.Get(csp.GetExtension().GetTypedConfig().GetTypeUrl())
		if cs == nil {
			if csp.GetIsOptional() {
				// "If a plugin is not supported but has is_optional set, then
				// we will ignore any routes that point to that plugin"
				cspCfgs[csp.GetExtension().GetName()] = nil
				continue
			}
			// "If no plugin is registered for it, the resource will be NACKed."
			// - RLS in xDS design
			return nil, fmt.Errorf("cluster specifier %q of type %q was not found", csp.GetExtension().GetName(), csp.GetExtension().GetTypedConfig().GetTypeUrl())
		}
		lbCfg, err := cs.ParseClusterSpecifierConfig(csp.GetExtension().GetTypedConfig())
		if err != nil {
			// "If a plugin is found, the value of the typed_config field will
			// be passed to it's conversion method, and if an error is
			// encountered, the resource will be NACKED." - RLS in xDS design
			return nil, fmt.Errorf("error: %q parsing config %q for cluster specifier %q of type %q", err, csp.GetExtension().GetTypedConfig(), csp.GetExtension().GetName(), csp.GetExtension().GetTypedConfig().GetTypeUrl())
		}
		// "If all cluster specifiers are valid, the xDS client will store the
		// configurations in a map keyed by the name of the extension instance." -
		// RLS in xDS Design
		cspCfgs[csp.GetExtension().GetName()] = lbCfg
	}
	return cspCfgs, nil
}

func generateRetryConfig(rp *v3routepb.RetryPolicy) (*RetryConfig, error) {
	if rp == nil {
		return nil, nil
	}

	cfg := &RetryConfig{RetryOn: make(map[codes.Code]bool)}
	for _, s := range strings.Split(rp.GetRetryOn(), ",") {
		switch strings.TrimSpace(strings.ToLower(s)) {
		case "cancelled":
			cfg.RetryOn[codes.Canceled] = true
		case "deadline-exceeded":
			cfg.RetryOn[codes.DeadlineExceeded] = true
		case "internal":
			cfg.RetryOn[codes.Internal] = true
		case "resource-exhausted":
			cfg.RetryOn[codes.ResourceExhausted] = true
		case "unavailable":
			cfg.RetryOn[codes.Unavailable] = true
		}
	}

	if rp.NumRetries == nil {
		cfg.NumRetries = 1
	} else {
		cfg.NumRetries = rp.GetNumRetries().Value
		if cfg.NumRetries < 1 {
			return nil, fmt.Errorf("retry_policy.num_retries = %v; must be >= 1", cfg.NumRetries)
		}
	}

	backoff := rp.GetRetryBackOff()
	if backoff == nil {
		cfg.RetryBackoff.BaseInterval = 25 * time.Millisecond
	} else {
		cfg.RetryBackoff.BaseInterval = backoff.GetBaseInterval().AsDuration()
		if cfg.RetryBackoff.BaseInterval <= 0 {
			return nil, fmt.Errorf("retry_policy.base_interval = %v; must be > 0", cfg.RetryBackoff.BaseInterval)
		}
	}
	if max := backoff.GetMaxInterval(); max == nil {
		cfg.RetryBackoff.MaxInterval = 10 * cfg.RetryBackoff.BaseInterval
	} else {
		cfg.RetryBackoff.MaxInterval = max.AsDuration()
		if cfg.RetryBackoff.MaxInterval <= 0 {
			return nil, fmt.Errorf("retry_policy.max_interval = %v; must be > 0", cfg.RetryBackoff.MaxInterval)
		}
	}

	if len(cfg.RetryOn) == 0 {
		return &RetryConfig{}, nil
	}
	return cfg, nil
}

func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecifier.BalancerConfig) ([]*Route, map[string]bool, error) {
	var routesRet []*Route
	var cspNames = make(map[string]bool)
	for _, r := range routes {
		match := r.GetMatch()
		if match == nil {
			return nil, nil, fmt.Errorf("route %+v doesn't have a match", r)
		}

		if len(match.GetQueryParameters()) != 0 {
			// Ignore route with query parameters.
			logger.Warningf("Ignoring route %+v with query parameter matchers", r)
			continue
		}

		pathSp := match.GetPathSpecifier()
		if pathSp == nil {
			return nil, nil, fmt.Errorf("route %+v doesn't have a path specifier", r)
		}

		var route Route
		switch pt := pathSp.(type) {
		case *v3routepb.RouteMatch_Prefix:
			route.Prefix = &pt.Prefix
		case *v3routepb.RouteMatch_Path:
			route.Path = &pt.Path
		case *v3routepb.RouteMatch_SafeRegex:
			regex := pt.SafeRegex.GetRegex()
			re, err := regexp.Compile(regex)
			if err != nil {
				return nil, nil, fmt.Errorf("route %+v contains an invalid regex %q", r, regex)
			}
			route.Regex = re
		default:
			return nil, nil, fmt.Errorf("route %+v has an unrecognized path specifier: %+v", r, pt)
		}

		if caseSensitive := match.GetCaseSensitive(); caseSensitive != nil {
			route.CaseInsensitive = !caseSensitive.Value
		}

		for _, h := range match.GetHeaders() {
			var header HeaderMatcher
			switch ht := h.GetHeaderMatchSpecifier().(type) {
			case *v3routepb.HeaderMatcher_ExactMatch:
				header.ExactMatch = &ht.ExactMatch
			case *v3routepb.HeaderMatcher_SafeRegexMatch:
				regex := ht.SafeRegexMatch.GetRegex()
				re, err := regexp.Compile(regex)
				if err != nil {
					return nil, nil, fmt.Errorf("route %+v contains an invalid regex %q", r, regex)
				}
				header.RegexMatch = re
			case *v3routepb.HeaderMatcher_RangeMatch:
				header.RangeMatch = &Int64Range{
					Start: ht.RangeMatch.Start,
					End:   ht.RangeMatch.End,
				}
			case *v3routepb.HeaderMatcher_PresentMatch:
				header.PresentMatch = &ht.PresentMatch
			case *v3routepb.HeaderMatcher_PrefixMatch:
				header.PrefixMatch = &ht.PrefixMatch
			case *v3routepb.HeaderMatcher_SuffixMatch:
				header.SuffixMatch = &ht.SuffixMatch
			case *v3routepb.HeaderMatcher_StringMatch:
				sm, err := matcher.StringMatcherFromProto(ht.StringMatch)
				if err != nil {
					return nil, nil, fmt.Errorf("route %+v has an invalid string matcher: %v", err, ht.StringMatch)
				}
				header.StringMatch = &sm
			default:
				return nil, nil, fmt.Errorf("route %+v has an unrecognized header matcher: %+v", r, ht)
			}
			header.Name = h.GetName()
			invert := h.GetInvertMatch()
			header.InvertMatch = &invert
			route.Headers = append(route.Headers, &header)
		}

		if fr := match.GetRuntimeFraction(); fr != nil {
			d := fr.GetDefaultValue()
			n := d.GetNumerator()
			switch d.GetDenominator() {
			case v3typepb.FractionalPercent_HUNDRED:
				n *= 10000
			case v3typepb.FractionalPercent_TEN_THOUSAND:
				n *= 100
			case v3typepb.FractionalPercent_MILLION:
			}
			route.Fraction = &n
		}

		switch r.GetAction().(type) {
		case *v3routepb.Route_Route:
			route.WeightedClusters = make(map[string]WeightedCluster)
			action := r.GetRoute()

			// Hash Policies are only applicable for a Ring Hash LB.
			if envconfig.XDSRingHash {
				hp, err := hashPoliciesProtoToSlice(action.HashPolicy)
				if err != nil {
					return nil, nil, err
				}
				route.HashPolicies = hp
			}

			switch a := action.GetClusterSpecifier().(type) {
			case *v3routepb.RouteAction_Cluster:
				route.WeightedClusters[a.Cluster] = WeightedCluster{Weight: 1}
			case *v3routepb.RouteAction_WeightedClusters:
				wcs := a.WeightedClusters
				var totalWeight uint64
				for _, c := range wcs.Clusters {
					w := c.GetWeight().GetValue()
					if w == 0 {
						continue
					}
					totalWeight += uint64(w)
					if totalWeight > math.MaxUint32 {
						return nil, nil, fmt.Errorf("xds: total weight of clusters exceeds MaxUint32")
					}
					wc := WeightedCluster{Weight: w}
					cfgs, err := processHTTPFilterOverrides(c.GetTypedPerFilterConfig())
					if err != nil {
						return nil, nil, fmt.Errorf("route %+v, action %+v: %v", r, a, err)
					}
					wc.HTTPFilterConfigOverride = cfgs
					route.WeightedClusters[c.GetName()] = wc
				}
				if totalWeight == 0 {
					return nil, nil, fmt.Errorf("route %+v, action %+v, has no valid cluster in WeightedCluster action", r, a)
				}
			case *v3routepb.RouteAction_ClusterSpecifierPlugin:
				// gRFC A28 was updated to say the following:
				//
				// The route’s action field must be route, and its
				// cluster_specifier:
				// - Can be Cluster
				// - Can be Weighted_clusters
				// - Can be unset or an unsupported field. The route containing
				//   this action will be ignored.
				//
				// This means that if this env var is not set, we should treat
				// it as if it we didn't know about the cluster_specifier_plugin
				// at all.
				if !envconfig.XDSRLS {
					logger.Warningf("Ignoring route %+v with unsupported route_action field: cluster_specifier_plugin", r)
					continue
				}
				if _, ok := csps[a.ClusterSpecifierPlugin]; !ok {
					// "When processing RouteActions, if any action includes a
					// cluster_specifier_plugin value that is not in
					// RouteConfiguration.cluster_specifier_plugins, the
					// resource will be NACKed." - RLS in xDS design
					return nil, nil, fmt.Errorf("route %+v, action %+v, specifies a cluster specifier plugin %+v that is not in Route Configuration", r, a, a.ClusterSpecifierPlugin)
				}
				if csps[a.ClusterSpecifierPlugin] == nil {
					logger.Warningf("Ignoring route %+v with optional and unsupported cluster specifier plugin %+v", r, a.ClusterSpecifierPlugin)
					continue
				}
				cspNames[a.ClusterSpecifierPlugin] = true
				route.ClusterSpecifierPlugin = a.ClusterSpecifierPlugin
			default:
				logger.Warningf("Ignoring route %+v with unknown ClusterSpecifier %+v", r, a)
				continue
			}

			msd := action.GetMaxStreamDuration()
			// Prefer grpc_timeout_header_max, if set.
			dur := msd.GetGrpcTimeoutHeaderMax()
			if dur == nil {
				dur = msd.GetMaxStreamDuration()
			}
			if dur != nil {
				d := dur.AsDuration()
				route.MaxStreamDuration = &d
			}

			var err error
			route.RetryConfig, err = generateRetryConfig(action.GetRetryPolicy())
			if err != nil {
				return nil, nil, fmt.Errorf("route %+v, action %+v: %v", r, action, err)
			}

			route.ActionType = RouteActionRoute

		case *v3routepb.Route_NonForwardingAction:
			// Expected to be used on server side.
			route.ActionType = RouteActionNonForwardingAction
		default:
			route.ActionType = RouteActionUnsupported
		}

		cfgs, err := processHTTPFilterOverrides(r.GetTypedPerFilterConfig())
		if err != nil {
			return nil, nil, fmt.Errorf("route %+v: %v", r, err)
		}
		route.HTTPFilterConfigOverride = cfgs
		routesRet = append(routesRet, &route)
	}
	return routesRet, cspNames, nil
}

func hashPoliciesProtoToSlice(policies []*v3routepb.RouteAction_HashPolicy) ([]*HashPolicy, error) {
	var hashPoliciesRet []*HashPolicy
	for _, p := range policies {
		policy := HashPolicy{Terminal: p.Terminal}
		switch p.GetPolicySpecifier().(type) {
		case *v3routepb.RouteAction_HashPolicy_Header_:
			policy.HashPolicyType = HashPolicyTypeHeader
			policy.HeaderName = p.GetHeader().GetHeaderName()
			if rr := p.GetHeader().GetRegexRewrite(); rr != nil {
				regex := rr.GetPattern().GetRegex()
				re, err := regexp.Compile(regex)
				if err != nil {
					return nil, fmt.Errorf("hash policy %+v contains an invalid regex %q", p, regex)
				}
				policy.Regex = re
				policy.RegexSubstitution = rr.GetSubstitution()
			}
		case *v3routepb.RouteAction_HashPolicy_FilterState_:
			if p.GetFilterState().GetKey() != "io.grpc.channel_id" {
				logger.Warningf("Ignoring hash policy %+v with invalid key for filter state policy %q", p, p.GetFilterState().GetKey())
				continue
			}
			policy.HashPolicyType = HashPolicyTypeChannelID
		default:
			logger.Warningf("Ignoring unsupported hash policy %T", p.GetPolicySpecifier())
			continue
		}

		hashPoliciesRet = append(hashPoliciesRet, &policy)
	}
	return hashPoliciesRet, nil
}

repository: grpc/grpc-go

license_type: permissive

repo_name: grpc/grpc-go

branch_name: refs/heads/master

gha_language: Go

extension: go


text:
package puzzle

import (
	"fmt"
	"strings"
)

// LineWidth the number of cells in each row, column and cube
const LineWidth = 9
const cubeWidth = LineWidth / 3

// Puzzle Representation of a Sudoku puzzle
type Puzzle [LineWidth][LineWidth]Square

// GetRow returns a given row
func (p Puzzle) GetRow(i int) Set {
	return p[i]
}

// GetColumn returns a column by given index
func (p Puzzle) GetColumn(i int) Set {
	var column Set
	for x := 0; x < LineWidth; x++ {
		column[x] = p[x][i]
	}
	return column
}

// GetCube returns squares that form a cube around given point
func (p Puzzle) GetCube(x, y int) Set {
	xOffset := x / cubeWidth
	yOffset := y / cubeWidth

	var result Set
	xIndex := xOffset * cubeWidth
	yIndex := yOffset * cubeWidth
	copy(result[0:3], p[xIndex][yIndex:yIndex+3])
	copy(result[3:6], p[1+xIndex][yIndex:yIndex+3])
	copy(result[6:9], p[2+xIndex][yIndex:yIndex+3])
	return result
}

// CalculatePossibilities Gives puzzle with updated possibility values
func (p Puzzle) CalculatePossibilities() Puzzle {
	var possByRow [LineWidth]Possibilies
	for row := 0; row < LineWidth; row++ {
		possByRow[row] = findElementPossbilities(p.GetRow(row))
	}

	var possByColumn [LineWidth]Possibilies
	for col := 0; col < LineWidth; col++ {
		possByColumn[col] = findElementPossbilities(p.GetColumn(col))
	}

	var possByCube [LineWidth]Possibilies
	for cube := 0; cube < LineWidth; cube++ {
		possByCube[cube] = findElementPossbilities(p.GetCube(cubePosition(cube)))
	}

	for x := 0; x < LineWidth; x++ {
		for y := 0; y < LineWidth; y++ {
			if !p[x][y].Solved() {
				p[x][y].Possibilities = mergePoss(possByRow[x], possByColumn[y], possByCube[cubeNumber(x, y)])
			}
		}
	}

	return p
}

func cubePosition(cubeNumber int) (int, int) {
	return (cubeNumber * cubeWidth) % LineWidth, (cubeNumber / cubeWidth) * cubeWidth
}

func cubeNumber(x, y int) int {
	return (x / cubeWidth) + (y/cubeWidth)*cubeWidth
}

func findElementPossbilities(elements Set) Possibilies {
	poss := Possibilies{true, true, true, true, true, true, true, true, true}

	for _, element := range elements {
		if element.Solved() {
			poss[element.Value-1] = false
		}
	}

	return poss
}

// PositionInCube Given an x,y, gives the position in a set
func PositionInCube(x, y int) int {
	return (x%cubeWidth)*cubeWidth + (y % cubeWidth)
}

func mergePoss(row, col, cube Possibilies) Possibilies {
	var result Possibilies
	for i := range result {
		result[i] = row[i] && col[i] && cube[i]
	}
	return result
}

func (p Puzzle) String() string {
	var output []string

	for _, r := range p {
		var line []string
		for _, v := range r {
			if v.Solved() {
				line = append(line, fmt.Sprintf("%v", v.Value))
			} else {
				line = append(line, "-")
			}
		}
		output = append(output, strings.Join(line[:], " "))
	}

	return strings.Join(output[:], "\n")
}

license: MIT

repository: truggeri/go-sudoku

repo_name: truggeri/go-sudoku


text:
/* ==UserStyle==
@name           让百度搜索内容相对居中
@namespace      USO Archive
@author         怒风玛法里奥
@description    `最大化情况下搜索结果距离页面左边20% 已匹配最新样式`
@version        202098.9.24
@license        NO-REDISTRIBUTION
@preprocessor   uso
==/UserStyle== */
@-moz-document url-prefix("https://www.baidu.com/s?"), url-prefix("https://www.baidu.com") {
/* 搜索标题 */
.s_form_wrapper {
		margin-left: 20%;
}
/* 导航栏 */
.s_tab_inner{
    margin-left: 21.3%;
}
/* 中间搜索结果整体左侧移动 */
#container{
	margin-left: 20% !important;
}
/* 去掉侧边新闻 */
.FYB_RD {
		display: none;
}

@media only screen and (max-width: 1500px) {
    .s_form_wrapper {
        margin-left: 17.2%;
    }
    .s_tab_inner{
        margin-left:19.3%;
    }
    #container {
		margin-left: 0%;
	}
}

}

branch_name: refs/heads/flomaster

gha_license_id: NOASSERTION

extension: css


text:
package ginplus

import (
	"bytes"
	"fmt"
	"github.com/iGeeky/open-account/pkg/baselib/errors"
	"github.com/iGeeky/open-account/pkg/baselib/log"
	"github.com/iGeeky/open-account/pkg/baselib/utils"
	"net/url"
	"regexp"
	"sort"
	"strings"

	"github.com/gin-gonic/gin"
)

// 需要签名的请求头
var defSignHeaders = []string{"host", "date"}
var signHeaderMap = map[string]bool{}

// 需要签名的请求头前缀
var gSignHeaderPrefix = "x-oa-"

type APISignConfig struct {
	Debug        bool
	CheckSign    bool
	DebugSignKey string
	AppKeys      map[string]string
	SignUrls     map[string]bool
}

// SignConfig global signature configuration
var SignConfig APISignConfig = APISignConfig{false, true, "", make(map[string]string), make(map[string]bool)}

func InitSign(signHeaders []string, signHeaderPrefix string) {
	if len(signHeaders) == 0 {
		signHeaders = defSignHeaders
	}
	for _, header := range signHeaders {
		signHeaderMap[strings.ToLower(header)] = true
	}
	if signHeaderPrefix != "" {
		gSignHeaderPrefix = strings.ToLower(signHeaderPrefix)
	}
}

var pattern *regexp.Regexp = regexp.MustCompile("\\%[0-9A-Fa-f]{2}")

func IsEncoded(str string) bool {
	find := pattern.FindString(str)
	return find != ""
}

func uriEncodeInternal(arg string, encodeSlash bool) string {
	if arg == "" || IsEncoded(arg) {
		return arg
	}

	chars := bytes.NewBuffer([]byte{})

	barg := []byte(arg)
	for _, ch := range barg {
		if (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.' {
			chars.WriteByte(ch)
		} else if ch == '/' {
			if encodeSlash {
				chars.WriteString("%2F")
			} else {
				chars.WriteByte(ch)
			}
		} else {
			chars.WriteString(fmt.Sprintf("%%%02X", ch))
		}
	}

	return chars.String()
}

func URI_ENCODE(uri string) string {
	return uriEncodeInternal(uri, false)
}

func uri_encode(uri string) string {
	return uriEncodeInternal(uri, true)
}

func createCanonicalArgs(args map[string][]string) string {
	if args == nil {
		return ""
	}
	var keys []string

	for k, _ := range args {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	keyValues := []string{}

	for _, key := range keys {
		value := args[key]
		if len(value) == 1 {
			keyValues = append(keyValues, uri_encode(key)+"="+uri_encode(value[0]))
			// keyValues.WriteString()
		} else { // is array
			sort.Strings(value)
			for _, valueSub := range value {
				// keyValues.WriteString(uri_encode(key) + "=" + uri_encode(valueSub))
				keyValues = append(keyValues, uri_encode(key)+"="+uri_encode(valueSub))
			}
		}
	}

	return strings.Join(keyValues, "&")
}

// map[string][]string
func createCanonicalHeaders(headers map[string][]string) (string, string) {
	lowerHeaders := make(map[string]string)
	signedHeaders := []string{}
	ignoreSignHeader := gSignHeaderPrefix + "sign"
	// log.Infof("---request headers: %+v", headers)
	for k, v := range headers {
		k = strings.ToLower(k)
		// TODO: value 是数组的情况。
		_, sign := signHeaderMap[k]
		// log.Infof("signHeaderMap: %+v, k: %s, sign: %v", signHeaderMap, k, sign)
		if k != ignoreSignHeader && (sign || strings.HasPrefix(k, gSignHeaderPrefix)) {
			signedHeaders = append(signedHeaders, k)
			sort.Strings(v)
			lowerHeaders[k] = strings.Join(v, ",")
		}
	}

	headerValues := bytes.NewBuffer([]byte{})
	sort.Strings(signedHeaders)
	for i, k := range signedHeaders {
		if i != 0 {
			headerValues.WriteString("\n")
		}
		headerValues.WriteString(k + ":" + strings.TrimSpace(lowerHeaders[k]))
	}
	return headerValues.String(), strings.Join(signedHeaders, ";")
}

func createSignStr(uri string, args map[string][]string, headers map[string][]string, bodyHash, appKey string) string {

	CanonicalURI := URI_ENCODE(uri)
	CanonicalArgs := createCanonicalArgs(args)
	CanonicalHeaders, SignedHeaders := createCanonicalHeaders(headers)

	SignStr := CanonicalURI + "\n" +
		CanonicalArgs + "\n" +
		CanonicalHeaders + "\n" +
		SignedHeaders + "\n" +
		bodyHash + "\n" +
		appKey

	return SignStr
}

func GetUriPath(uri string) (string, error) {
	myurl, err := url.Parse(uri)
	if err != nil {
		return "", err
	}
	return myurl.Path, err
}

var EMPTY_BODY []byte = []byte("")

//哈希函数. 输入数据, 输出该数据的hex编码后的hash值.
type HexHashFunc func(data []byte) string

func SignEx(uri string, args map[string][]string, headers map[string][]string, body []byte, appKey string, hash HexHashFunc) (signature string, signStr string) {
	path, err := GetUriPath(uri)
	if err != nil {
		path = uri
	}

	if body == nil {
		body = EMPTY_BODY
	}
	bodyHash := hash(body)
	signStr = createSignStr(path, args, headers, bodyHash, appKey)
	btSignStr := []byte(signStr)
	signature = hash(btSignStr)

	return
}

func Sign(uri string, args map[string][]string, headers map[string][]string, body []byte, appKey string) (signature string, signStr string) {
	signature, signStr = SignEx(uri, args, headers, body, appKey, utils.Sha1hex)
	return
}

func SignSimple(uri string, args map[string]string, headers map[string]string, body []byte, appKey string) (signature string, signStr string) {
	myargs := make(map[string][]string)
	for k, v := range args {
		value := make([]string, 1)
		value[0] = v
		myargs[k] = value
	}

	myheaders := make(map[string][]string)
	for k, v := range headers {
		value := make([]string, 1)
		value[0] = v
		myheaders[k] = value
	}
	signature, signStr = Sign(uri, myargs, myheaders, body, appKey)
	return
}

// GetSignKey get signature key by appID
func (a *APISignConfig) GetSignKey(appID string) string {
	return a.AppKeys[appID]
}

// apiSignCheck check api signature
func apiSignCheck(ctx *ContextPlus, body []byte) bool {
	_ = ctx.Request.ParseForm()
	uri := ctx.Request.RequestURI
	args := ctx.Request.Form

	appID := ctx.GetCustomHeader("AppID")
	if appID == "" {
		log.Errorf("header '%s' missing.", ctx.CustomHeaderName("AppID"))
		ctx.JSON(401, gin.H{"ok": false, "reason": errors.ErrArgsInvalid})
		ctx.Abort()
		return false
	}
	ctx.Set("appID", appID)

	appKey := SignConfig.GetSignKey(appID)
	if appKey == "" {
		log.Errorf("Unknown appID [%s]", appID)
		ctx.JSON(401, gin.H{"ok": false, "reason": errors.ErrArgsInvalid})
		ctx.Abort()
		return false
	}

	reqSign := ctx.GetCustomHeader("Sign")
	if reqSign == "" {
		log.Errorf("header '%s' missing.", ctx.CustomHeaderName("Sign"))
		ctx.JSON(401, gin.H{"ok": false, "reason": errors.ErrSignError})
		ctx.Abort()
		return false
	}
	// log.Errorf("reqSign: %s", reqSign)
	// 测试工具使用。
	if SignConfig.DebugSignKey != "" && reqSign == SignConfig.DebugSignKey && SignConfig.Debug {
		return true
	}

	signature, SignStr := SignEx(uri, args, ctx.Request.Header, body, appKey, utils.Sha1hex)
	if signature != reqSign {
		log.Errorf("reqSign: [%s] != serverSign: [%s] \nSignStr [[%s]]", reqSign, signature, SignStr)
		log.Infof("req body len: %d", len(body))
		if SignConfig.Debug && len(body) < 100 {
			log.Infof("body: [[%v]]", string(body))
		}
		resp := gin.H{"ok": false, "reason": errors.ErrSignError}
		ctx.JSON(401, resp)
		ctx.Abort()
		return false
	}
	return true
}

// SignCheck check the signature
func SignCheck(c *gin.Context) {
	if c.Request.Method == "OPTIONS" {
		c.Next()
		return
	}
	ctx := NewContetPlus(c)
	uri := ctx.GetURI()

	if SignConfig.CheckSign && SignConfig.SignUrls[uri] {
		body, _ := ctx.GetBody()
		apiSignCheck(ctx, body)
	}

	c.Next()

}

repository: iGeeky/open-account

repo_name: iGeeky/open-account


text:
package api

import (
	"context"

	pb "github.com/massnetorg/MassNet-wallet/api/proto"
	"github.com/massnetorg/MassNet-wallet/logging"
)

func (s *Server) GetClientStatus(ctx context.Context, in *pb.GetClientStatusRequest) (*pb.GetClientStatusResponse, error) {
	logging.CPrint(logging.INFO, "a request is received to query the status of client", logging.LogFormat{})

	resp := &pb.GetClientStatusResponse{
		PeerListening:   s.syncManager.Switch().IsListening(),
		Syncing:         !s.syncManager.IsCaughtUp(),
		LocalBestHeight: s.syncManager.Chain.BestBlockHeight(),
		ChainID:         s.syncManager.Chain.ChainID().String(),
	}

	if bestPeer := s.syncManager.BestPeer(); bestPeer != nil {
		resp.KnownBestHeight = bestPeer.Height
	}
	if resp.LocalBestHeight > resp.KnownBestHeight {
		resp.KnownBestHeight = resp.LocalBestHeight
	}

	var outCount, inCount uint32
	resp.Peers = &pb.GetClientStatusResponsePeerList{
		Outbound: make([]*pb.GetClientStatusResponsePeerInfo, 0),
		Inbound:  make([]*pb.GetClientStatusResponsePeerInfo, 0),
		Other:    make([]*pb.GetClientStatusResponsePeerInfo, 0),
	}
	for _, info := range s.syncManager.GetPeerInfos() {
		peer := &pb.GetClientStatusResponsePeerInfo{
			Id:      info.ID,
			Address: info.RemoteAddr,
		}
		if info.IsOutbound {
			outCount++
			peer.Direction = "outbound"
			resp.Peers.Outbound = append(resp.Peers.Outbound, peer)
			continue
		}
		inCount++
		peer.Direction = "inbound"
		resp.Peers.Inbound = append(resp.Peers.Inbound, peer)
	}
	resp.PeerCount = &pb.GetClientStatusResponsePeerCountInfo{Total: outCount + inCount, Outbound: outCount, Inbound: inCount}

	logging.CPrint(logging.INFO, "GetClientStatus completed", logging.LogFormat{})
	return resp, nil
}

repository: SuperElon/MassNet-wallet

repo_name: SuperElon/MassNet-wallet


text:
DESCRIPTION = "This module provides various portable helper functions for module building \
modules."

SECTION = "libs"
LICENSE = "Artistic-1.0 | GPL-1.0-or-later"
PR = "r0"

MAINTAINER=	"Poky <poky@yoctoproject.org>"
HOMEPAGE=	"https://metacpan.org/release/ExtUtils-Helpers"

LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Artistic-1.0;md5=cda03bbdc3c1951996392b872397b798 \
file://${COMMON_LICENSE_DIR}/GPL-1.0-or-later;md5=30c0b8a5048cc2f4be5ff15ef0d8cf61"

SRC_URI = "https://cpan.metacpan.org/authors/id/L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz"

SRC_URI[md5sum] = "83b00c1e401321c425ae5db6b2b2fd12"
SRC_URI[sha256sum] = "de901b6790a4557cf4ec908149e035783b125bf115eb9640feb1bc1c24c33416"

S = "${WORKDIR}/ExtUtils-Helpers-${PV}"

inherit cpan allarch

BBCLASSEXTEND = "native"

repository: rehsack/meta-cpan

repo_name: rehsack/meta-cpan

gha_license_id: MIT

gha_language: BitBake

extension: bb


text:
DESCRIPTION = "AliOS Things Linux Edition ywssapp"
SUMMARY = "AliOS Things Linux Edition ywssapp"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e"

SRC_URI = "file://linkkit-0.6.tar.gz \
           file://ywss.init \
           file://linkkit.kv"

# only support sysvinit for now.
inherit update-rc.d

INITSCRIPT_NAME = "ywss"
INITSCRIPT_PARAMS = "defaults 87"

TARGET_CC_ARCH += "${LDFLAGS}"
CFLAGS_prepend = "-O0 -g "

do_compile() {
    make ARCH=${TARGET_ARCH} -f Makefile.ywss wifi_module=rtk
}

do_install() {
    # create linkkit dir
    install -d ${D}/${bindir}/
    install -d ${D}/${datadir}/linkkit/
    install -d ${D}/${INIT_D_DIR}
    install -m 0644 ${WORKDIR}/linkkit.kv ${D}/${datadir}/linkkit/linkkit.kv
    install -m 0755 ${WORKDIR}/ywss.init ${D}/${INIT_D_DIR}/ywss
    install -m 0755 ${B}/ywssapp ${D}/${bindir}/ywssapp
}

repository: livvol/AliOS-Things-Linux-Edition

repo_name: livvol/AliOS-Things-Linux-Edition