<commit_before>// Copyright 2017 Monax Industries Limited
//
// 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 config

const headerCopyright = `# Copyright 2017 Monax Industries Limited
#
# 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.

# This is a TOML configuration for Eris-DB chains generated by Eris.

`

const sectionServiceGeneral = `[service]
# NOTE: this section is read by Eris tooling, and ignored by eris-db.
# Image specifies the image name eris-cli needs to pull
# for running the chain.
image = "{{.ChainImageName}}"
# Define whether eris-cli needs to attach the data container
# for the chain.
data_container = {{.UseDataContainer}}
# Specify a list of ports that need to be exported on the container.
ports = {{.ExportedPorts}}
{{ if ne .ContainerEntrypoint "" }}# Entrypoint points to the default action to execute
# in the chain container.
entry_point = "{{.ContainerEntrypoint}}"{{ end }}

`

const sectionServiceDependencies = `[dependencies]
# NOTE: this section is read by Eris tooling, and ignored by eris-db.
# Eris-db expects these services to be available; eric-cli tooling will
# automatically set these services up for you.
# Services to boot with/required by the chain
services = [ "keys" ]

`

const sectionChainGeneral = `[chain]

# ChainId is a human-readable name to identify the chain.
# This must correspond to the chain_id defined in the genesis file
# and the assertion here provides a safe-guard on misconfiguring chains.
assert_chain_id = "{{.AssertChainId}}"
# semantic major and minor version
major_version = {{.ErisdbMajorVersion}}
minor_version = {{.ErisdbMinorVersion}}
# genesis file, relative path is to eris-db working directory
genesis_file = "{{.GenesisRelativePath}}"

`

const separatorChainConsensus = `
################################################################################
##
##  consensus
##
################################################################################

`

const sectionChainConsensus = `  [chain.consensus]
  # consensus defines the module to use for consensus and
  # this will define the peer-to-peer consensus network;
  # accepted values are ("noops", "abci",) "tendermint"
  name = "{{.Name}}"
  # version is the major and minor semantic version;
  # the version will be asserted on
  major_version = {{.MajorVersion}}
  minor_version = {{.MinorVersion}}
  # relative path to consensus' module root folder
  relative_root = "{{.ModuleRelativeRoot}}"

  `

const separatorChainApplicationManager = `
################################################################################
##
##  application manager
##
################################################################################

`

const sectionChainApplicationManager = `  [chain.manager]
  # application manager name defines the module to use for handling
  # the transactions.  Supported names are "erismint"
  name = "{{.Name}}"
  # version is the major and minor semantic version;
  # the version will be asserted on
  major_version = {{.MajorVersion}}
  minor_version = {{.MinorVersion}}
  # relative path to application manager root folder
  relative_root = "{{.ModuleRelativeRoot}}"

  `

const separatorServerConfiguration = `
################################################################################
################################################################################
##
## Server configurations
##
################################################################################
################################################################################

`

// TODO: [ben] map entries to structure defined in eris-db
const sectionServers = `[servers]

  [servers.bind]
  address = ""
  port = 1337

  [servers.tls]
  tls = false
  cert_path = ""
  key_path = ""

  [servers.cors]
  enable = false
  allow_origins = []
  allow_credentials = false
  allow_methods = []
  allow_headers = []
  expose_headers = []
  max_age = 0

  [servers.http]
  json_rpc_endpoint = "/rpc"

  [servers.websocket]
  endpoint = "/socketrpc"
  max_sessions = 50
  read_buffer_size = 4096
  write_buffer_size = 4096

	[servers.tendermint]
	# Multiple listeners can be separated with a comma
	rpc_local_address = "0.0.0.0:46657"
	endpoint = "/websocket"

  [servers.logging]
  console_log_level = "info"
  file_log_level = "warn"
  log_file = ""

  `

const separatorModules = `
################################################################################
################################################################################
##
## Module configurations - dynamically loaded based on chain configuration
##
################################################################################
################################################################################

`

// TODO: [ben] make configurable
const sectionabci = `
################################################################################
##
## Tendermint Socket Protocol (abci)
## version 0.6.0
##
## abci expects a tendermint consensus process to run and connect to Eris-DB
##
################################################################################

[abci]
# listener address for accepting tendermint socket protocol connections
listener = "tcp://0.0.0.0:46658"

`

// TODO: [ben] minimal fields have been made configurable; expand where needed
const sectionTendermint = `
################################################################################
##
## Tendermint
## version 0.6
##
## in-process execution of Tendermint consensus engine
##
################################################################################

[tendermint]
# private validator file is used by tendermint to keep the status
# of the private validator, but also (currently) holds the private key
# for the private vaildator to sign with.  This private key needs to be moved
# out and directly managed by eris-keys
# This file needs to be in the root directory
private_validator_file = "priv_validator.json"

  # Tendermint requires additional configuration parameters.
  # Eris-DB's tendermint consensus module will load [tendermint.configuration]
  # as the configuration for Tendermint.
  # Eris-DB will respect the configurations set in this file where applicable,
  # but reserves the option to override or block conflicting settings.
  [tendermint.configuration]
  # moniker is the name of the node on the tendermint p2p network
  moniker = "{{.Moniker}}"
  # seeds lists the peers tendermint can connect to join the network
  seeds = "{{.Seeds}}"
  # fast_sync allows a tendermint node to catch up faster when joining
  # the network.
  # NOTE: Tendermint has reported potential issues with fast_sync enabled.
  # The recommended setting is for keeping it disabled.
  fast_sync = {{.FastSync}}
  # database backend to use for Tendermint. Supported "leveldb" and "memdb".
  db_backend = "leveldb"
  # logging level. Supported "error" < "warn" < "notice" < "info" < "debug"
  log_level = "info"
  # node local address
  node_laddr = "0.0.0.0:46656"
  # rpc local address
	# NOTE: value is ignored when run in-process as RPC is
	# handled by [servers.tendermint]
  rpc_laddr = "0.0.0.0:46657"
  # proxy application address - used for abci connections,
  # and this port should not be exposed for in-process Tendermint
  proxy_app = "tcp://127.0.0.1:46658"

  # Extended Tendermint configuration settings
  # for reference to Tendermint see https://github.com/tendermint/tendermint/blob/master/config/tendermint/config.go

  # genesis_file = "./data/tendermint/genesis.json"
  # skip_upnp = false
  # addrbook_file = "./data/tendermint/addrbook.json"
  # priv_validator_file = "./data/tendermint/priv_validator.json"
  # db_dir = "./data/tendermint/data"
  # prof_laddr = ""
  # revision_file = "./data/tendermint/revision"
  # cswal = "./data/tendermint/data/cswal"
  # cswal_light = false

  # block_size = 10000
  # disable_data_hash = false
  # timeout_propose = 3000
  # timeout_propose_delta = 500
  # timeout_prevote = 1000
  # timeout_prevote_delta = 500
  # timeout_precommit = 1000
  # timeout_precommit_delta = 500
  # timeout_commit = 1000
  # mempool_recheck = true
  # mempool_recheck_empty = true
  # mempool_broadcast = true

		[tendermint.configuration.p2p]
		# Switch config keys
		dial_timeout_seconds = 3
		handshake_timeout_seconds = 20
		max_num_peers = 20
		authenticated_encryption = true

		# MConnection config keys
		send_rate = 512000
		recv_rate = 512000

		# Fuzz params
		fuzz_enable = false # use the fuzz wrapped conn
		fuzz_active = false # toggle fuzzing
		fuzz_mode = "drop"  # eg. drop, delay
		fuzz_max_delay_milliseconds = 3000
		fuzz_prob_drop_rw = 0.2
		fuzz_prob_drop_conn = 0.00
		fuzz_prob_sleep = 0.00

`

const sectionErisMint = `
################################################################################
##
## Eris-Mint
## version 0.16
##
## The original Ethereum virtual machine with IAVL merkle trees
## and tendermint/go-wire encoding
##
################################################################################

[erismint]
# Database backend to use for ErisMint state database.
# Supported "leveldb" and "memdb".
db_backend = "leveldb"
# tendermint host address needs to correspond to tendermints configuration
# of the rpc local address
tendermint_host = "0.0.0.0:46657"

`
<commit_msg>config: tiny cleanup in config template<commit_after>// Copyright 2017 Monax Industries Limited
//
// 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 config

const headerCopyright = `# Copyright 2017 Monax Industries Limited
#
# 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.

# This is a TOML configuration for Eris-DB chains generated by Eris.

`

const sectionServiceGeneral = `[service]
# NOTE: this section is read by Eris tooling, and ignored by eris-db.
# Image specifies the image name eris-cli needs to pull
# for running the chain.
image = "{{.ChainImageName}}"
# Define whether eris-cli needs to attach the data container
# for the chain.
data_container = {{.UseDataContainer}}
# Specify a list of ports that need to be exported on the container.
ports = {{.ExportedPorts}}
{{ if ne .ContainerEntrypoint "" }}# Entrypoint points to the default action to execute
# in the chain container.
entry_point = "{{.ContainerEntrypoint}}"{{ end }}

`

const sectionServiceDependencies = `[dependencies]
# NOTE: this section is read by Eris tooling, and ignored by eris-db.
# Eris-db expects these services to be available; eric-cli tooling will
# automatically set these services up for you.
# Services to boot with/required by the chain
services = [ "keys" ]

`

const sectionChainGeneral = `[chain]

# ChainId is a human-readable name to identify the chain.
# This must correspond to the chain_id defined in the genesis file
# and the assertion here provides a safe-guard on misconfiguring chains.
assert_chain_id = "{{.AssertChainId}}"
# semantic major and minor version
major_version = {{.ErisdbMajorVersion}}
minor_version = {{.ErisdbMinorVersion}}
# genesis file, relative path is to eris-db working directory
genesis_file = "{{.GenesisRelativePath}}"

`

const separatorChainConsensus = `
################################################################################
##
##  consensus
##
################################################################################

`

const sectionChainConsensus = `  [chain.consensus]
  # consensus defines the module to use for consensus and
  # this will define the peer-to-peer consensus network;
  # accepted values are ("noops", "abci",) "tendermint"
  name = "{{.Name}}"
  # version is the major and minor semantic version;
  # the version will be asserted on
  major_version = {{.MajorVersion}}
  minor_version = {{.MinorVersion}}
  # relative path to consensus' module root folder
  relative_root = "{{.ModuleRelativeRoot}}"

  `

const separatorChainApplicationManager = `
################################################################################
##
##  application manager
##
################################################################################

`

const sectionChainApplicationManager = `  [chain.manager]
  # application manager name defines the module to use for handling
  # the transactions.  Supported names are "erismint"
  name = "{{.Name}}"
  # version is the major and minor semantic version;
  # the version will be asserted on
  major_version = {{.MajorVersion}}
  minor_version = {{.MinorVersion}}
  # relative path to application manager root folder
  relative_root = "{{.ModuleRelativeRoot}}"

  `

const separatorServerConfiguration = `
################################################################################
################################################################################
##
## Server configurations
##
################################################################################
################################################################################

`

// TODO: [ben] map entries to structure defined in eris-db
const sectionServers = `[servers]

  [servers.bind]
  address = ""
  port = 1337

  [servers.tls]
  tls = false
  cert_path = ""
  key_path = ""

  [servers.cors]
  enable = false
  allow_origins = []
  allow_credentials = false
  allow_methods = []
  allow_headers = []
  expose_headers = []
  max_age = 0

  [servers.http]
  json_rpc_endpoint = "/rpc"

  [servers.websocket]
  endpoint = "/socketrpc"
  max_sessions = 50
  read_buffer_size = 4096
  write_buffer_size = 4096

	[servers.tendermint]
	# Multiple listeners can be separated with a comma
	rpc_local_address = "0.0.0.0:46657"
	endpoint = "/websocket"

  [servers.logging]
  console_log_level = "info"
  file_log_level = "warn"
  log_file = ""

  `

const separatorModules = `
################################################################################
################################################################################
##
## Module configurations - dynamically loaded based on chain configuration
##
################################################################################
################################################################################

`

// TODO: [ben] minimal fields have been made configurable; expand where needed
const sectionTendermint = `
################################################################################
##
## Tendermint
## version 0.8
##
## in-process execution of Tendermint consensus engine
##
################################################################################

[tendermint]
# private validator file is used by tendermint to keep the status
# of the private validator, but also (currently) holds the private key
# for the private vaildator to sign with.  This private key needs to be moved
# out and directly managed by eris-keys
# This file needs to be in the root directory
private_validator_file = "priv_validator.json"

  # Tendermint requires additional configuration parameters.
  # Eris-DB's tendermint consensus module will load [tendermint.configuration]
  # as the configuration for Tendermint.
  # Eris-DB will respect the configurations set in this file where applicable,
  # but reserves the option to override or block conflicting settings.
  [tendermint.configuration]
  # moniker is the name of the node on the tendermint p2p network
  moniker = "{{.Moniker}}"
  # seeds lists the peers tendermint can connect to join the network
  seeds = "{{.Seeds}}"
  # fast_sync allows a tendermint node to catch up faster when joining
  # the network.
  # NOTE: Tendermint has reported potential issues with fast_sync enabled.
  # The recommended setting is for keeping it disabled.
  fast_sync = {{.FastSync}}
  # database backend to use for Tendermint. Supported "leveldb" and "memdb".
  db_backend = "leveldb"
  # logging level. Supported "error" < "warn" < "notice" < "info" < "debug"
  log_level = "info"
  # node local address
  node_laddr = "0.0.0.0:46656"
  # rpc local address
	# NOTE: value is ignored when run in-process as RPC is
	# handled by [servers.tendermint]
  rpc_laddr = "0.0.0.0:46657"
  # proxy application address - used for abci connections,
  # and this port should not be exposed for in-process Tendermint
  proxy_app = "tcp://127.0.0.1:46658"

  # Extended Tendermint configuration settings
  # for reference to Tendermint see https://github.com/tendermint/tendermint/blob/master/config/tendermint/config.go

  # genesis_file = "./data/tendermint/genesis.json"
  # skip_upnp = false
  # addrbook_file = "./data/tendermint/addrbook.json"
  # priv_validator_file = "./data/tendermint/priv_validator.json"
  # db_dir = "./data/tendermint/data"
  # prof_laddr = ""
  # revision_file = "./data/tendermint/revision"
  # cswal = "./data/tendermint/data/cswal"
  # cswal_light = false

  # block_size = 10000
  # disable_data_hash = false
  # timeout_propose = 3000
  # timeout_propose_delta = 500
  # timeout_prevote = 1000
  # timeout_prevote_delta = 500
  # timeout_precommit = 1000
  # timeout_precommit_delta = 500
  # timeout_commit = 1000
  # mempool_recheck = true
  # mempool_recheck_empty = true
  # mempool_broadcast = true

		[tendermint.configuration.p2p]
		# Switch config keys
		dial_timeout_seconds = 3
		handshake_timeout_seconds = 20
		max_num_peers = 20
		authenticated_encryption = true

		# MConnection config keys
		send_rate = 512000
		recv_rate = 512000

		# Fuzz params
		fuzz_enable = false # use the fuzz wrapped conn
		fuzz_active = false # toggle fuzzing
		fuzz_mode = "drop"  # eg. drop, delay
		fuzz_max_delay_milliseconds = 3000
		fuzz_prob_drop_rw = 0.2
		fuzz_prob_drop_conn = 0.00
		fuzz_prob_sleep = 0.00

`

const sectionErisMint = `
################################################################################
##
## Eris-Mint
## version 0.16
##
## The original Ethereum virtual machine with IAVL merkle trees
## and tendermint/go-wire encoding
##
################################################################################

[erismint]
# Database backend to use for ErisMint state database.
# Supported "leveldb" and "memdb".
db_backend = "leveldb"
# tendermint host address needs to correspond to tendermints configuration
# of the rpc local address
tendermint_host = "0.0.0.0:46657"

`
<|endoftext|>


<commit_before>// Copyright 2021 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"context"
	"database/sql"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/pingcap/tidb-tools/pkg/dbutil"
	pd "github.com/tikv/pd/client"
	"go.etcd.io/etcd/clientv3"
	"go.uber.org/zap"
)

const (
	tidbServerInformationPath = "/tidb/server/info"
	defaultEtcdDialTimeOut    = 3 * time.Second

	defaultGCSafePointTTL = 5 * 60
)

var (
	tidbVersionRegex       = regexp.MustCompile(`-[v]?\d+\.\d+\.\d+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`)
	autoGCSafePointVersion = semver.New("4.0.0")
)

func getPDDDLIDs(pCtx context.Context, cli *clientv3.Client) ([]string, error) {
	ctx, cancel := context.WithTimeout(pCtx, 10*time.Second)
	defer cancel()

	resp, err := cli.Get(ctx, tidbServerInformationPath, clientv3.WithPrefix())
	if err != nil {
		return nil, errors.Trace(err)
	}
	pdDDLIds := make([]string, len(resp.Kvs))
	for i, kv := range resp.Kvs {
		items := strings.Split(string(kv.Key), "/")
		pdDDLIds[i] = items[len(items)-1]
	}
	return pdDDLIds, nil
}

// getTiDBDDLIDs gets DDL IDs from TiDB
func getTiDBDDLIDs(ctx context.Context, db *sql.DB) ([]string, error) {
	query := "SELECT * FROM information_schema.tidb_servers_info;"
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return []string{}, errors.Annotatef(err, "sql: %s", query)
	}
	return GetSpecifiedColumnValueAndClose(rows, "DDL_ID")
}

func checkSameCluster(ctx context.Context, db *sql.DB, pdAddrs []string) (bool, error) {
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   pdAddrs,
		DialTimeout: defaultEtcdDialTimeOut,
	})
	if err != nil {
		return false, errors.Trace(err)
	}
	tidbDDLIDs, err := getTiDBDDLIDs(ctx, db)
	if err != nil {
		return false, err
	}
	pdDDLIDs, err := getPDDDLIDs(ctx, cli)
	if err != nil {
		return false, err
	}
	sort.Strings(tidbDDLIDs)
	sort.Strings(pdDDLIDs)

	return sameStringArray(tidbDDLIDs, pdDDLIDs), nil
}

func sameStringArray(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// GetPDClientForGC is an initialization step.
func GetPDClientForGC(ctx context.Context, db *sql.DB) (pd.Client, error) {
	if ok, _ := dbutil.IsTiDB(ctx, db); ok {
		pdAddrs, err := GetPDAddrs(ctx, db)
		if err != nil {
			return nil, err
		}
		if len(pdAddrs) > 0 {
			if same, err := checkSameCluster(ctx, db, pdAddrs); err != nil {
				log.Info("[automatically GC] check whether fetched pd addr and TiDB belong to one cluster failed", zap.Strings("pd address", pdAddrs), zap.Error(err))
			} else if same {
				pdClient, err := pd.NewClientWithContext(ctx, pdAddrs, pd.SecurityOption{})
				if err != nil {
					log.Info("[automatically GC] create pd client to control GC failed", zap.Strings("pd address", pdAddrs), zap.Error(err))
					return nil, err
				}
				return pdClient, nil
			}
		}
	}
	return nil, nil
}

// GetPDAddrs gets PD address from TiDB
func GetPDAddrs(ctx context.Context, db *sql.DB) ([]string, error) {
	query := "SELECT * FROM information_schema.cluster_info where type = 'pd';"
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return []string{}, errors.Annotatef(err, "sql: %s", query)
	}
	return GetSpecifiedColumnValueAndClose(rows, "STATUS_ADDRESS")
}

// GetSpecifiedColumnValueAndClose get columns' values whose name is equal to columnName and close the given rows
func GetSpecifiedColumnValueAndClose(rows *sql.Rows, columnName string) ([]string, error) {
	if rows == nil {
		return []string{}, nil
	}
	defer rows.Close()
	columnName = strings.ToUpper(columnName)
	var strs []string
	columns, _ := rows.Columns()
	addr := make([]interface{}, len(columns))
	oneRow := make([]sql.NullString, len(columns))
	fieldIndex := -1
	for i, col := range columns {
		if strings.ToUpper(col) == columnName {
			fieldIndex = i
		}
		addr[i] = &oneRow[i]
	}
	if fieldIndex == -1 {
		return strs, nil
	}
	for rows.Next() {
		err := rows.Scan(addr...)
		if err != nil {
			return strs, errors.Trace(err)
		}
		if oneRow[fieldIndex].Valid {
			strs = append(strs, oneRow[fieldIndex].String)
		}
	}
	return strs, errors.Trace(rows.Err())
}

// StartGCSavepointUpdateService keeps GC safePoint stop moving forward.
func StartGCSavepointUpdateService(ctx context.Context, pdCli pd.Client, db *sql.DB, snapshot string) error {
	versionStr, err := selectVersion(db)
	if err != nil {
		log.Info("detect version of tidb failed")
		return nil
	}
	versionStr = tidbVersionRegex.FindString(versionStr)[1:]
	versionStr = strings.TrimPrefix(versionStr, "v")
	tidbVersion, err := semver.NewVersion(versionStr)
	if err != nil {
		log.Info("parse version of tidb failed")
		return nil
	}
	// get latest snapshot
	snapshotTS, err := parseSnapshotToTSO(db, snapshot)
	if tidbVersion.Compare(*autoGCSafePointVersion) > 0 {
		log.Info("tidb support auto gc safepoint", zap.Stringer("version", tidbVersion))
		if err != nil {
			return err
		}
		go updateServiceSafePoint(ctx, pdCli, snapshotTS)
	} else {
		log.Info("tidb doesn't support auto gc safepoint", zap.Stringer("version", tidbVersion))
	}
	return nil
}

func updateServiceSafePoint(ctx context.Context, pdClient pd.Client, snapshotTS uint64) {
	updateInterval := time.Duration(defaultGCSafePointTTL/2) * time.Second
	tick := time.NewTicker(updateInterval)
	DiffServiceSafePointID := fmt.Sprintf("Sync_diff_%d", time.Now().UnixNano())
	log.Info("generate dumpling gc safePoint id", zap.String("id", DiffServiceSafePointID))
	for {
		log.Debug("update PD safePoint limit with ttl",
			zap.Uint64("safePoint", snapshotTS),
			zap.Duration("updateInterval", updateInterval))
		for retryCnt := 0; retryCnt <= 10; retryCnt++ {
			_, err := pdClient.UpdateServiceGCSafePoint(ctx, DiffServiceSafePointID, defaultGCSafePointTTL, snapshotTS)
			if err == nil {
				break
			}
			log.Debug("update PD safePoint failed", zap.Error(err), zap.Int("retryTime", retryCnt))
			select {
			case <-ctx.Done():
				return
			case <-time.After(time.Second):
			}
		}
		select {
		case <-ctx.Done():
			return
		case <-tick.C:
		}
	}
}

func parseSnapshotToTSO(pool *sql.DB, snapshot string) (uint64, error) {
	snapshotTS, err := strconv.ParseUint(snapshot, 10, 64)
	if err == nil {
		return snapshotTS, nil
	}
	var tso sql.NullInt64
	query := "SELECT unix_timestamp(?)"
	row := pool.QueryRow(query, snapshot)
	err = row.Scan(&tso)
	if err != nil {
		return 0, errors.Annotatef(err, "sql: %s", strings.ReplaceAll(query, "?", fmt.Sprintf(`"%s"`, snapshot)))
	}
	if !tso.Valid {
		return 0, errors.Errorf("snapshot %s format not supported. please use tso or '2006-01-02 15:04:05' format time", snapshot)
	}
	return uint64(tso.Int64 * 1000) << 18, nil
}

func GetSnapshot(ctx context.Context, db *sql.DB) ([]string, error) {
	query := "SHOW MASTER STATUS;"
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return []string{}, errors.Annotatef(err, "sql: %s", query)
	}
	return GetSpecifiedColumnValueAndClose(rows, "Position")
}

func selectVersion(db *sql.DB) (string, error) {
	var versionInfo string
	const query = "SELECT version()"
	row := db.QueryRow(query)
	err := row.Scan(&versionInfo)
	if err != nil {
		return "", errors.Annotatef(err, "sql: %s", query)
	}
	return versionInfo, nil
}
<commit_msg>make fmt<commit_after>// Copyright 2021 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"context"
	"database/sql"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/pingcap/tidb-tools/pkg/dbutil"
	pd "github.com/tikv/pd/client"
	"go.etcd.io/etcd/clientv3"
	"go.uber.org/zap"
)

const (
	tidbServerInformationPath = "/tidb/server/info"
	defaultEtcdDialTimeOut    = 3 * time.Second

	defaultGCSafePointTTL = 5 * 60
)

var (
	tidbVersionRegex       = regexp.MustCompile(`-[v]?\d+\.\d+\.\d+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`)
	autoGCSafePointVersion = semver.New("4.0.0")
)

func getPDDDLIDs(pCtx context.Context, cli *clientv3.Client) ([]string, error) {
	ctx, cancel := context.WithTimeout(pCtx, 10*time.Second)
	defer cancel()

	resp, err := cli.Get(ctx, tidbServerInformationPath, clientv3.WithPrefix())
	if err != nil {
		return nil, errors.Trace(err)
	}
	pdDDLIds := make([]string, len(resp.Kvs))
	for i, kv := range resp.Kvs {
		items := strings.Split(string(kv.Key), "/")
		pdDDLIds[i] = items[len(items)-1]
	}
	return pdDDLIds, nil
}

// getTiDBDDLIDs gets DDL IDs from TiDB
func getTiDBDDLIDs(ctx context.Context, db *sql.DB) ([]string, error) {
	query := "SELECT * FROM information_schema.tidb_servers_info;"
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return []string{}, errors.Annotatef(err, "sql: %s", query)
	}
	return GetSpecifiedColumnValueAndClose(rows, "DDL_ID")
}

func checkSameCluster(ctx context.Context, db *sql.DB, pdAddrs []string) (bool, error) {
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   pdAddrs,
		DialTimeout: defaultEtcdDialTimeOut,
	})
	if err != nil {
		return false, errors.Trace(err)
	}
	tidbDDLIDs, err := getTiDBDDLIDs(ctx, db)
	if err != nil {
		return false, err
	}
	pdDDLIDs, err := getPDDDLIDs(ctx, cli)
	if err != nil {
		return false, err
	}
	sort.Strings(tidbDDLIDs)
	sort.Strings(pdDDLIDs)

	return sameStringArray(tidbDDLIDs, pdDDLIDs), nil
}

func sameStringArray(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// GetPDClientForGC is an initialization step.
func GetPDClientForGC(ctx context.Context, db *sql.DB) (pd.Client, error) {
	if ok, _ := dbutil.IsTiDB(ctx, db); ok {
		pdAddrs, err := GetPDAddrs(ctx, db)
		if err != nil {
			return nil, err
		}
		if len(pdAddrs) > 0 {
			if same, err := checkSameCluster(ctx, db, pdAddrs); err != nil {
				log.Info("[automatically GC] check whether fetched pd addr and TiDB belong to one cluster failed", zap.Strings("pd address", pdAddrs), zap.Error(err))
			} else if same {
				pdClient, err := pd.NewClientWithContext(ctx, pdAddrs, pd.SecurityOption{})
				if err != nil {
					log.Info("[automatically GC] create pd client to control GC failed", zap.Strings("pd address", pdAddrs), zap.Error(err))
					return nil, err
				}
				return pdClient, nil
			}
		}
	}
	return nil, nil
}

// GetPDAddrs gets PD address from TiDB
func GetPDAddrs(ctx context.Context, db *sql.DB) ([]string, error) {
	query := "SELECT * FROM information_schema.cluster_info where type = 'pd';"
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return []string{}, errors.Annotatef(err, "sql: %s", query)
	}
	return GetSpecifiedColumnValueAndClose(rows, "STATUS_ADDRESS")
}

// GetSpecifiedColumnValueAndClose get columns' values whose name is equal to columnName and close the given rows
func GetSpecifiedColumnValueAndClose(rows *sql.Rows, columnName string) ([]string, error) {
	if rows == nil {
		return []string{}, nil
	}
	defer rows.Close()
	columnName = strings.ToUpper(columnName)
	var strs []string
	columns, _ := rows.Columns()
	addr := make([]interface{}, len(columns))
	oneRow := make([]sql.NullString, len(columns))
	fieldIndex := -1
	for i, col := range columns {
		if strings.ToUpper(col) == columnName {
			fieldIndex = i
		}
		addr[i] = &oneRow[i]
	}
	if fieldIndex == -1 {
		return strs, nil
	}
	for rows.Next() {
		err := rows.Scan(addr...)
		if err != nil {
			return strs, errors.Trace(err)
		}
		if oneRow[fieldIndex].Valid {
			strs = append(strs, oneRow[fieldIndex].String)
		}
	}
	return strs, errors.Trace(rows.Err())
}

// StartGCSavepointUpdateService keeps GC safePoint stop moving forward.
func StartGCSavepointUpdateService(ctx context.Context, pdCli pd.Client, db *sql.DB, snapshot string) error {
	versionStr, err := selectVersion(db)
	if err != nil {
		log.Info("detect version of tidb failed")
		return nil
	}
	versionStr = tidbVersionRegex.FindString(versionStr)[1:]
	versionStr = strings.TrimPrefix(versionStr, "v")
	tidbVersion, err := semver.NewVersion(versionStr)
	if err != nil {
		log.Info("parse version of tidb failed")
		return nil
	}
	// get latest snapshot
	snapshotTS, err := parseSnapshotToTSO(db, snapshot)
	if tidbVersion.Compare(*autoGCSafePointVersion) > 0 {
		log.Info("tidb support auto gc safepoint", zap.Stringer("version", tidbVersion))
		if err != nil {
			return err
		}
		go updateServiceSafePoint(ctx, pdCli, snapshotTS)
	} else {
		log.Info("tidb doesn't support auto gc safepoint", zap.Stringer("version", tidbVersion))
	}
	return nil
}

func updateServiceSafePoint(ctx context.Context, pdClient pd.Client, snapshotTS uint64) {
	updateInterval := time.Duration(defaultGCSafePointTTL/2) * time.Second
	tick := time.NewTicker(updateInterval)
	DiffServiceSafePointID := fmt.Sprintf("Sync_diff_%d", time.Now().UnixNano())
	log.Info("generate dumpling gc safePoint id", zap.String("id", DiffServiceSafePointID))
	for {
		log.Debug("update PD safePoint limit with ttl",
			zap.Uint64("safePoint", snapshotTS),
			zap.Duration("updateInterval", updateInterval))
		for retryCnt := 0; retryCnt <= 10; retryCnt++ {
			_, err := pdClient.UpdateServiceGCSafePoint(ctx, DiffServiceSafePointID, defaultGCSafePointTTL, snapshotTS)
			if err == nil {
				break
			}
			log.Debug("update PD safePoint failed", zap.Error(err), zap.Int("retryTime", retryCnt))
			select {
			case <-ctx.Done():
				return
			case <-time.After(time.Second):
			}
		}
		select {
		case <-ctx.Done():
			return
		case <-tick.C:
		}
	}
}

func parseSnapshotToTSO(pool *sql.DB, snapshot string) (uint64, error) {
	snapshotTS, err := strconv.ParseUint(snapshot, 10, 64)
	if err == nil {
		return snapshotTS, nil
	}
	var tso sql.NullInt64
	query := "SELECT unix_timestamp(?)"
	row := pool.QueryRow(query, snapshot)
	err = row.Scan(&tso)
	if err != nil {
		return 0, errors.Annotatef(err, "sql: %s", strings.ReplaceAll(query, "?", fmt.Sprintf(`"%s"`, snapshot)))
	}
	if !tso.Valid {
		return 0, errors.Errorf("snapshot %s format not supported. please use tso or '2006-01-02 15:04:05' format time", snapshot)
	}
	return uint64(tso.Int64*1000) << 18, nil
}

func GetSnapshot(ctx context.Context, db *sql.DB) ([]string, error) {
	query := "SHOW MASTER STATUS;"
	rows, err := db.QueryContext(ctx, query)
	if err != nil {
		return []string{}, errors.Annotatef(err, "sql: %s", query)
	}
	return GetSpecifiedColumnValueAndClose(rows, "Position")
}

func selectVersion(db *sql.DB) (string, error) {
	var versionInfo string
	const query = "SELECT version()"
	row := db.QueryRow(query)
	err := row.Scan(&versionInfo)
	if err != nil {
		return "", errors.Annotatef(err, "sql: %s", query)
	}
	return versionInfo, nil
}
<|endoftext|>


<commit_before>package weave

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"net"
	"time"
)

const (
	InitialInterval = 5 * time.Second
	MaxInterval     = 10 * time.Minute
)

const (
	CMInitiate   = iota
	CMTerminated = iota
	CMRefresh    = iota
	CMStatus     = iota
)

func StartConnectionMaker(router *Router) *ConnectionMaker {
	queryChan := make(chan *ConnectionMakerInteraction, ChannelSize)
	state := &ConnectionMaker{
		router:         router,
		queryChan:      queryChan,
		cmdLineAddress: make(map[string]bool),
		targets:        make(map[string]*Target)}
	go state.queryLoop(queryChan)
	return state
}

func (cm *ConnectionMaker) InitiateConnection(address string) {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMInitiate},
		address:     address}
}

func (cm *ConnectionMaker) ConnectionTerminated(address string) {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMTerminated},
		address:     address}
}

func (cm *ConnectionMaker) Refresh() {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMRefresh}}
}

func (cm *ConnectionMaker) String() string {
	resultChan := make(chan interface{}, 0)
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMStatus, resultChan: resultChan}}
	result := <-resultChan
	return result.(string)
}

func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) {
	timer := time.NewTimer(MaxDuration)
	run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) }
	for {
		select {
		case query, ok := <-queryChan:
			if !ok {
				return
			}
			switch query.code {
			case CMInitiate:
				cm.cmdLineAddress[NormalisePeerAddr(query.address)] = true
				run()
			case CMTerminated:
				if target, found := cm.targets[query.address]; found {
					target.attempting = false
					target.tryAfter, target.tryInterval = tryAfter(target.tryInterval)
				}
				run()
			case CMRefresh:
				run()
			case CMStatus:
				run()
				query.resultChan <- cm.status()
			default:
				log.Fatal("Unexpected connection maker query:", query)
			}
		case <-timer.C:
			run()
		}
	}
}

func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
	ourself := cm.router.Ourself
	validTarget := make(map[string]bool)

	// copy the set of things we are connected to, so we can access them without locking
	our_connected_peers := make(map[PeerName]bool)
	our_connected_targets := make(map[string]bool)
	ourself.ForEachConnection(func(peer PeerName, conn Connection) {
		our_connected_peers[peer] = true
		our_connected_targets[conn.RemoteTCPAddr()] = true
	})

	addTarget := func(address string) {
		if !our_connected_targets[address] {
			validTarget[address] = true
			cm.addTarget(address)
		}
	}

	// Add command-line targets that are not connected
	for address, _ := range cm.cmdLineAddress {
		addTarget(address)
	}

	// Add targets for peers that someone else is connected to, but we
	// aren't
	cm.router.Peers.ForEach(func(name PeerName, peer *Peer) {
		peer.ForEachConnection(func(otherPeer PeerName, conn Connection) {
			if otherPeer == ourself.Name || our_connected_peers[otherPeer] {
				return
			}
			address := conn.RemoteTCPAddr()
			// try both portnumber of connection and standard port
			addTarget(address)
			if host, _, err := net.SplitHostPort(address); err == nil {
				addTarget(NormalisePeerAddr(host))
			}
		})
	})

	now := time.Now() // make sure we catch items just added
	after := MaxDuration
	for address, target := range cm.targets {
		if our_connected_targets[address] {
			delete(cm.targets, address)
			continue
		}
		if target.attempting {
			continue
		}
		if !validTarget[address] {
			delete(cm.targets, address)
			continue
		}
		switch duration := target.tryAfter.Sub(now); {
		case duration <= 0:
			target.attempting = true
			go cm.attemptConnection(address, cm.cmdLineAddress[address])
		case duration < after:
			after = duration
		}
	}
	return after
}

func (cm *ConnectionMaker) addTarget(address string) {
	if _, found := cm.targets[address]; !found {
		target := &Target{}
		target.tryAfter, target.tryInterval = tryImmediately()
		cm.targets[address] = target
	}
}

func (cm *ConnectionMaker) status() string {
	var buf bytes.Buffer
	for address, target := range cm.targets {
		var fmtStr string
		if target.attempting {
			fmtStr = "%s (trying since %v)\n"
		} else {
			fmtStr = "%s (next try at %v)\n"
		}
		buf.WriteString(fmt.Sprintf(fmtStr, address, target.tryAfter))
	}
	return buf.String()
}

func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
	log.Println("Attempting connection to", address)
	if err := cm.router.Ourself.CreateConnection(address, acceptNewPeer); err != nil {
		log.Println(err)
		cm.ConnectionTerminated(address)
	}
}

func tryImmediately() (time.Time, time.Duration) {
	interval := time.Duration(rand.Int63n(int64(InitialInterval)))
	return time.Now(), interval
}

func tryAfter(interval time.Duration) (time.Time, time.Duration) {
	interval += time.Duration(rand.Int63n(int64(interval)))
	if interval > MaxInterval {
		interval = MaxInterval
	}
	return time.Now().Add(interval), interval
}
<commit_msg>Resolve name to IP address when removing connected targets<commit_after>package weave

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"net"
	"time"
)

const (
	InitialInterval = 5 * time.Second
	MaxInterval     = 10 * time.Minute
)

const (
	CMInitiate   = iota
	CMTerminated = iota
	CMRefresh    = iota
	CMStatus     = iota
)

func StartConnectionMaker(router *Router) *ConnectionMaker {
	queryChan := make(chan *ConnectionMakerInteraction, ChannelSize)
	state := &ConnectionMaker{
		router:         router,
		queryChan:      queryChan,
		cmdLineAddress: make(map[string]bool),
		targets:        make(map[string]*Target)}
	go state.queryLoop(queryChan)
	return state
}

func (cm *ConnectionMaker) InitiateConnection(address string) {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMInitiate},
		address:     address}
}

func (cm *ConnectionMaker) ConnectionTerminated(address string) {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMTerminated},
		address:     address}
}

func (cm *ConnectionMaker) Refresh() {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMRefresh}}
}

func (cm *ConnectionMaker) String() string {
	resultChan := make(chan interface{}, 0)
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMStatus, resultChan: resultChan}}
	result := <-resultChan
	return result.(string)
}

func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) {
	timer := time.NewTimer(MaxDuration)
	run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) }
	for {
		select {
		case query, ok := <-queryChan:
			if !ok {
				return
			}
			switch query.code {
			case CMInitiate:
				cm.cmdLineAddress[NormalisePeerAddr(query.address)] = true
				run()
			case CMTerminated:
				if target, found := cm.targets[query.address]; found {
					target.attempting = false
					target.tryAfter, target.tryInterval = tryAfter(target.tryInterval)
				}
				run()
			case CMRefresh:
				run()
			case CMStatus:
				run()
				query.resultChan <- cm.status()
			default:
				log.Fatal("Unexpected connection maker query:", query)
			}
		case <-timer.C:
			run()
		}
	}
}

func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
	ourself := cm.router.Ourself
	validTarget := make(map[string]bool)

	// copy the set of things we are connected to, so we can access them without locking
	our_connected_peers := make(map[PeerName]bool)
	our_connected_targets := make(map[string]bool)
	ourself.ForEachConnection(func(peer PeerName, conn Connection) {
		our_connected_peers[peer] = true
		our_connected_targets[conn.RemoteTCPAddr()] = true
	})

	addTarget := func(address string) {
		if !our_connected_targets[address] {
			validTarget[address] = true
			cm.addTarget(address)
		}
	}

	// Add command-line targets that are not connected
	for address, _ := range cm.cmdLineAddress {
		addTarget(address)
	}

	// Add targets for peers that someone else is connected to, but we
	// aren't
	cm.router.Peers.ForEach(func(name PeerName, peer *Peer) {
		peer.ForEachConnection(func(otherPeer PeerName, conn Connection) {
			if otherPeer == ourself.Name || our_connected_peers[otherPeer] {
				return
			}
			address := conn.RemoteTCPAddr()
			// try both portnumber of connection and standard port
			addTarget(address)
			if host, _, err := net.SplitHostPort(address); err == nil {
				addTarget(NormalisePeerAddr(host))
			}
		})
	})

	now := time.Now() // make sure we catch items just added
	after := MaxDuration
	for address, target := range cm.targets {
		tcpAddr, err := net.ResolveTCPAddr("tcp4", address)
		ipAddr := tcpAddr.String()
		if err == nil && our_connected_targets[ipAddr] {
			delete(cm.targets, address)
			continue
		}
		if target.attempting {
			continue
		}
		if !validTarget[address] {
			delete(cm.targets, address)
			continue
		}
		switch duration := target.tryAfter.Sub(now); {
		case duration <= 0:
			target.attempting = true
			go cm.attemptConnection(address, cm.cmdLineAddress[address])
		case duration < after:
			after = duration
		}
	}
	return after
}

func (cm *ConnectionMaker) addTarget(address string) {
	if _, found := cm.targets[address]; !found {
		target := &Target{}
		target.tryAfter, target.tryInterval = tryImmediately()
		cm.targets[address] = target
	}
}

func (cm *ConnectionMaker) status() string {
	var buf bytes.Buffer
	for address, target := range cm.targets {
		var fmtStr string
		if target.attempting {
			fmtStr = "%s (trying since %v)\n"
		} else {
			fmtStr = "%s (next try at %v)\n"
		}
		buf.WriteString(fmt.Sprintf(fmtStr, address, target.tryAfter))
	}
	return buf.String()
}

func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
	log.Println("Attempting connection to", address)
	if err := cm.router.Ourself.CreateConnection(address, acceptNewPeer); err != nil {
		log.Println(err)
		cm.ConnectionTerminated(address)
	}
}

func tryImmediately() (time.Time, time.Duration) {
	interval := time.Duration(rand.Int63n(int64(InitialInterval)))
	return time.Now(), interval
}

func tryAfter(interval time.Duration) (time.Time, time.Duration) {
	interval += time.Duration(rand.Int63n(int64(interval)))
	if interval > MaxInterval {
		interval = MaxInterval
	}
	return time.Now().Add(interval), interval
}
<|endoftext|>


<commit_before>// Copyright 2014  Author. 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.

// 把中间件和原本的路由处理器封装在一起， 先执行中间件，如果中间件没有提前结束请求， 最终会把执行权归还给原本的路由处理器。
// 中间件允许注册多个，执行顺序和注册顺序一致。 其实原本的路由处理器也可以看做一个中间件了，不过，它是放在最后一个执行位置上（除了末尾的空中间件）。
// 参考开源项目：https://github.com/urfave/negroni
//
//
//   http.HandleFunc("/log", func)
//   mw := middleware.New()
//   mw.RegisterMiddlewareHandleFunc(Recovery, Token)
//   mw.Run(":9999" )
//
// OR
//
/*package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})

	n := middleware.New()
	n.MuxHandler(mux)
	n.RegisterMiddlewareHandleFunc(Middleware1,Middleware2)
        n.Bootstrap()
	http.ListenAndServe(":3000", n)
}*/
//

package middleware

import "net/http"

//中间件接口
type MiddleWare interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type MiddleWareFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h MiddleWareFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

//中间件列表结构
//适配器模式
type middlewareHandler struct {
	handler MiddleWare
	next    *middlewareHandler
}

func (m *middlewareHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	if (nil == m.handler) {
		return
	}
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

//包装标准库的http.Handler
func Wrap(handler http.Handler) MiddleWare {
	return MiddleWareFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}

//Cbping是一堆中间件处理程序管理器，
//可以当作http.handler被调用
//通过RegisterMiddlewareHandleFunc|RegisterMiddleWare注册中间件
type Cbping struct {
	//链表头
	//由中间件和路由处理器组建而成
	//路由处理器处于链表末端（除了末尾的空中间件）
	middlewareHead middlewareHandler
	//中间件数组
	middlewares    []MiddleWare

	//路由处理器
	//原始路由处理器
	mux            http.Handler
}

func (c *Cbping) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	//解析参数
	r.ParseForm()
	//中间件处理
	c.middlewareHead.ServeHTTP(rw, r)
}

//引导初始
func (c *Cbping)Bootstrap() {
	if (nil == c.mux) {
		c.mux = http.DefaultServeMux
	}
	c.middlewares = append(c.middlewares, Wrap(c.mux))
	c.middlewareHead = build(c.middlewares)
}

//运行
func (c *Cbping)Run(addr string) {
	c.Bootstrap()
	http.ListenAndServe(addr, c)
}

func (c *Cbping)RegisterMiddlewareHandleFunc(handlers... func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
	for _, handler := range handlers {
		c.RegisterMiddleWare(MiddleWareFunc(handler))
	}

}

//注册中间件
//中间件执行顺序和注册顺序一致
func (c *Cbping)RegisterMiddleWare(handler MiddleWare) {
	c.middlewares = append(c.middlewares, handler)
}

//注册原本路由处理器
func (c *Cbping)MuxHandler(muxHandler http.Handler) {
	c.mux = muxHandler
}

func New() *Cbping {
	return &Cbping{}
}

//递归构建执行链表
func build(handlers []MiddleWare) middlewareHandler {
	var next middlewareHandler

	if len(handlers) == 0 {
		return voidMiddlewareHandler()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddlewareHandler()
	}

	return middlewareHandler{handlers[0], &next}
}

func voidMiddlewareHandler() middlewareHandler {
	return middlewareHandler{
		MiddleWareFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middlewareHandler{},
	}
}<commit_msg>Update middleware.go<commit_after>// Copyright 2016  Author. 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.

// 把中间件和原本的路由处理器封装在一起， 先执行中间件，如果中间件没有提前结束请求， 最终会把执行权归还给原本的路由处理器。
// 中间件允许注册多个，执行顺序和注册顺序一致。 其实原本的路由处理器也可以看做一个中间件了，不过，它是放在最后一个执行位置上（除了末尾的空中间件）。
// 参考开源项目：https://github.com/urfave/negroni
//
//
//   http.HandleFunc("/log", func)
//   mw := middleware.New()
//   mw.RegisterMiddlewareHandleFunc(Recovery, Token)
//   mw.Run(":9999" )
//
// OR
//
/*package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Welcome to the home page!")
	})

	n := middleware.New()
	n.MuxHandler(mux)
	n.RegisterMiddlewareHandleFunc(Middleware1,Middleware2)
        n.Bootstrap()
	http.ListenAndServe(":3000", n)
}*/
//

package middleware

import "net/http"

//中间件接口
type MiddleWare interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type MiddleWareFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h MiddleWareFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

//中间件列表结构
//适配器模式
type middlewareHandler struct {
	handler MiddleWare
	next    *middlewareHandler
}

func (m *middlewareHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	if (nil == m.handler) {
		return
	}
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

//包装标准库的http.Handler
func Wrap(handler http.Handler) MiddleWare {
	return MiddleWareFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}

//Cbping是一堆中间件处理程序管理器，
//可以当作http.handler被调用
//通过RegisterMiddlewareHandleFunc|RegisterMiddleWare注册中间件
type Cbping struct {
	//链表头
	//由中间件和路由处理器组建而成
	//路由处理器处于链表末端（除了末尾的空中间件）
	middlewareHead middlewareHandler
	//中间件数组
	middlewares    []MiddleWare

	//路由处理器
	//原始路由处理器
	mux            http.Handler
}

func (c *Cbping) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	//解析参数
	r.ParseForm()
	//中间件处理
	c.middlewareHead.ServeHTTP(rw, r)
}

//引导初始
func (c *Cbping)Bootstrap() {
	if (nil == c.mux) {
		c.mux = http.DefaultServeMux
	}
	c.middlewares = append(c.middlewares, Wrap(c.mux))
	c.middlewareHead = build(c.middlewares)
}

//运行
func (c *Cbping)Run(addr string) {
	c.Bootstrap()
	http.ListenAndServe(addr, c)
}

func (c *Cbping)RegisterMiddlewareHandleFunc(handlers... func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
	for _, handler := range handlers {
		c.RegisterMiddleWare(MiddleWareFunc(handler))
	}

}

//注册中间件
//中间件执行顺序和注册顺序一致
func (c *Cbping)RegisterMiddleWare(handler MiddleWare) {
	c.middlewares = append(c.middlewares, handler)
}

//注册原本路由处理器
func (c *Cbping)MuxHandler(muxHandler http.Handler) {
	c.mux = muxHandler
}

func New() *Cbping {
	return &Cbping{}
}

//递归构建执行链表
func build(handlers []MiddleWare) middlewareHandler {
	var next middlewareHandler

	if len(handlers) == 0 {
		return voidMiddlewareHandler()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddlewareHandler()
	}

	return middlewareHandler{handlers[0], &next}
}

func voidMiddlewareHandler() middlewareHandler {
	return middlewareHandler{
		MiddleWareFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middlewareHandler{},
	}
}
<|endoftext|>


<commit_before>package middleware

import (
	"net/http"

	ctx "github.com/gorilla/context"
	"github.com/jordan-wright/gophish/auth"
	"github.com/jordan-wright/gophish/db"
)

// GetContext wraps each request in a function which fills in the context for a given request.
// This includes setting the User and Session keys and values as necessary for use in later functions.
func GetContext(handler http.Handler) http.HandlerFunc {
	// Set the context here
	return func(w http.ResponseWriter, r *http.Request) {
		// Parse the request form
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Error parsing request", http.StatusInternalServerError)
		}
		// Set the context appropriately here.
		// Set the session
		session, _ := auth.Store.Get(r, "gophish")
		// Put the session in the context so that
		ctx.Set(r, "session", session)
		if id, ok := session.Values["id"]; ok {
			u, err := db.GetUser(id.(int64))
			if err != nil {
				ctx.Set(r, "user", nil)
			}
			ctx.Set(r, "user", u)
		} else {
			ctx.Set(r, "user", nil)
		}
		handler.ServeHTTP(w, r)
		// Remove context contents
		ctx.Clear(r)
	}
}

func RequireAPIKey(handler http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		ak := r.Form.Get("api_key")
		if ak == "" {
			JSONError(w, 500, "API Key not set")
		} else {
			id, err := db.Conn.SelectInt("SELECT id FROM users WHERE api_key=?", ak)
			if id == 0 || err != nil {
				JSONError(w, 500, "Invalid API Key")
				return
			}
			ctx.Set(r, "user_id", id)
			ctx.Set(r, "api_key", ak)
			handler.ServeHTTP(w, r)
		}
	}
}

// RequireLogin is a simple middleware which checks to see if the user is currently logged in.
// If not, the function returns a 302 redirect to the login page.
func RequireLogin(handler http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if u := ctx.Get(r, "user"); u != nil {
			handler.ServeHTTP(w, r)
		} else {
			http.Redirect(w, r, "/login", 302)
		}
	}
}

func JSONError(w http.ResponseWriter, c int, m string) {
	http.Error(w, m, c)
}
<commit_msg>Fixed invalid/unset API Key header to be 400 instead of 500 Successfully handle OPTIONS header for API<commit_after>package middleware

import (
	"net/http"

	ctx "github.com/gorilla/context"
	"github.com/jordan-wright/gophish/auth"
	"github.com/jordan-wright/gophish/db"
)

// GetContext wraps each request in a function which fills in the context for a given request.
// This includes setting the User and Session keys and values as necessary for use in later functions.
func GetContext(handler http.Handler) http.HandlerFunc {
	// Set the context here
	return func(w http.ResponseWriter, r *http.Request) {
		// Parse the request form
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Error parsing request", http.StatusInternalServerError)
		}
		// Set the context appropriately here.
		// Set the session
		session, _ := auth.Store.Get(r, "gophish")
		// Put the session in the context so that
		ctx.Set(r, "session", session)
		if id, ok := session.Values["id"]; ok {
			u, err := db.GetUser(id.(int64))
			if err != nil {
				ctx.Set(r, "user", nil)
			}
			ctx.Set(r, "user", u)
		} else {
			ctx.Set(r, "user", nil)
		}
		handler.ServeHTTP(w, r)
		// Remove context contents
		ctx.Clear(r)
	}
}

func RequireAPIKey(handler http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		ak := r.Form.Get("api_key")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		if r.Method == "OPTIONS" {
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
			w.Header().Set("Access-Control-Max-Age", "1000")
			w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
			return
		}
		if ak == "" {
			JSONError(w, 400, "API Key not set")
		} else {
			id, err := db.Conn.SelectInt("SELECT id FROM users WHERE api_key=?", ak)
			if id == 0 || err != nil {
				JSONError(w, 400, "Invalid API Key")
				return
			}
			ctx.Set(r, "user_id", id)
			ctx.Set(r, "api_key", ak)
			handler.ServeHTTP(w, r)
		}
	}
}

// RequireLogin is a simple middleware which checks to see if the user is currently logged in.
// If not, the function returns a 302 redirect to the login page.
func RequireLogin(handler http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if u := ctx.Get(r, "user"); u != nil {
			handler.ServeHTTP(w, r)
		} else {
			http.Redirect(w, r, "/login", 302)
		}
	}
}

func JSONError(w http.ResponseWriter, c int, m string) {
	http.Error(w, m, c)
}
<|endoftext|>


<commit_before>package outputs

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/elastic/libbeat/common"
	"github.com/elastic/libbeat/logp"
)

type PublisherType struct {
	name                string
	tags                string
	disabled            bool
	Index               string
	Output              []OutputInterface
	TopologyOutput      OutputInterface
	ElasticsearchOutput ElasticsearchOutputType
	RedisOutput         RedisOutputType
	FileOutput          FileOutputType
	IgnoreOutgoing      bool

	RefreshTopologyTimer <-chan time.Time
	Queue                chan common.MapStr
}

type MothershipConfig struct {
	Enabled            bool
	Save_topology      bool
	Host               string
	Port               int
	Protocol           string
	Username           string
	Password           string
	Index              string
	Path               string
	Db                 int
	Db_topology        int
	Timeout            int
	Reconnect_interval int
	Filename           string
	Rotate_every_kb    int
	Number_of_files    int
	DataType           string
	Flush_interval     int
}

type AgentConfig struct {
	Name                  string
	Refresh_topology_freq int
	Ignore_outgoing       bool
	Topology_expire       int
	Tags                  []string
}

var Publisher PublisherType

type Topology struct {
	Name string `json:"name"`
	Ip   string `json:"ip"`
}

func PrintPublishEvent(event common.MapStr) {
	json, err := json.MarshalIndent(event, "", "  ")
	if err != nil {
		logp.Err("json.Marshal: %s", err)
	} else {
		logp.Debug("publish", "Publish: %s", string(json))
	}
}

func (publisher *PublisherType) GetServerName(ip string) string {
	// in case the IP is localhost, return current agent name
	islocal, err := common.IsLoopback(ip)
	if err != nil {
		logp.Err("Parsing IP %s fails with: %s", ip, err)
		return ""
	} else {
		if islocal {
			return publisher.name
		}
	}
	// find the agent with the desired IP
	if publisher.TopologyOutput != nil {
		return publisher.TopologyOutput.GetNameByIP(ip)
	} else {
		return ""
	}
}

func (publisher *PublisherType) publishFromQueue() {
	for mapstr := range publisher.Queue {
		err := publisher.publishEvent(mapstr)
		if err != nil {
			logp.Err("Publishing failed: %v", err)
		}
	}
}

func (publisher *PublisherType) publishEvent(event common.MapStr) error {

	// the timestamp is mandatory
	ts, ok := event["timestamp"].(common.Time)
	if !ok {
		return errors.New("Missing 'timestamp' field from event.")
	}

	// the count is mandatory
	err := event.EnsureCountField()
	if err != nil {
		return err
	}

	// the type is mandatory
	_, ok = event["type"].(string)
	if !ok {
		return errors.New("Missing 'type' field from event.")
	}

	var src_server, dst_server string
	src, ok := event["src"].(*common.Endpoint)
	if ok {
		src_server = publisher.GetServerName(src.Ip)
		event["client_ip"] = src.Ip
		event["client_port"] = src.Port
		event["client_proc"] = src.Proc
		event["client_server"] = src_server
		delete(event, "src")
	}
	dst, ok := event["dst"].(*common.Endpoint)
	if ok {
		dst_server = publisher.GetServerName(dst.Ip)
		event["ip"] = dst.Ip
		event["port"] = dst.Port
		event["proc"] = dst.Proc
		event["server"] = dst_server
		delete(event, "dst")
	}

	if publisher.IgnoreOutgoing && dst_server != "" &&
		dst_server != publisher.name {
		// duplicated transaction -> ignore it
		logp.Debug("publish", "Ignore duplicated transaction on %s: %s -> %s", publisher.name, src_server, dst_server)
		return nil
	}

	event["agent"] = publisher.name
	if len(publisher.tags) > 0 {
		event["tags"] = publisher.tags
	}

	if _GeoLite != nil {
		real_ip, exists := event["real_ip"]
		if exists && len(real_ip.(string)) > 0 {
			loc := _GeoLite.GetLocationByIP(real_ip.(string))
			if loc != nil && loc.Latitude != 0 && loc.Longitude != 0 {
				event["client_location"] = fmt.Sprintf("%f, %f", loc.Latitude, loc.Longitude)
			}
		} else {
			if len(src_server) == 0 && src != nil { // only for external IP addresses
				loc := _GeoLite.GetLocationByIP(src.Ip)
				if loc != nil && loc.Latitude != 0 && loc.Longitude != 0 {
					event["client_location"] = fmt.Sprintf("%f, %f", loc.Latitude, loc.Longitude)
				}
			}
		}
	}

	if logp.IsDebug("publish") {
		PrintPublishEvent(event)
	}

	// add transaction
	has_error := false
	if !publisher.disabled {
		for i := 0; i < len(publisher.Output); i++ {
			err := publisher.Output[i].PublishEvent(time.Time(ts), event)
			if err != nil {
				logp.Err("Fail to publish event type on output %s: %s", publisher.Output, err)
				has_error = true
			}
		}
	}

	if has_error {
		return errors.New("Fail to publish event")
	}
	return nil
}

func (publisher *PublisherType) UpdateTopologyPeriodically() {
	for _ = range publisher.RefreshTopologyTimer {
		publisher.PublishTopology()
	}
}

func (publisher *PublisherType) PublishTopology(params ...string) error {

	var localAddrs []string = params

	if len(params) == 0 {
		addrs, err := common.LocalIpAddrsAsStrings(false)
		if err != nil {
			logp.Err("Getting local IP addresses fails with: %s", err)
			return err
		}
		localAddrs = addrs
	}

	if publisher.TopologyOutput != nil {
		logp.Debug("publish", "Add topology entry for %s: %s", publisher.name, localAddrs)

		err := publisher.TopologyOutput.PublishIPs(publisher.name, localAddrs)
		if err != nil {
			return err
		}
	}

	return nil
}

func (publisher *PublisherType) Init(publishDisabled bool,
	outputs map[string]MothershipConfig, agent AgentConfig) error {

	var err error
	publisher.IgnoreOutgoing = agent.Ignore_outgoing

	publisher.disabled = publishDisabled
	if publisher.disabled {
		logp.Info("Dry run mode. All output types except the file based one are disabled.")
	}

	output, exists := outputs["elasticsearch"]
	if exists && output.Enabled && !publisher.disabled {
		err := publisher.ElasticsearchOutput.Init(output,
			agent.Topology_expire)
		if err != nil {
			logp.Err("Fail to initialize Elasticsearch as output: %s", err)
			return err
		}
		publisher.Output = append(publisher.Output, OutputInterface(&publisher.ElasticsearchOutput))

		if output.Save_topology {
			if publisher.TopologyOutput != nil {
				logp.Err("Multiple outputs defined to store topology. Please add save_topology = true option only for one output.")
				return errors.New("Multiple outputs defined to store topology")
			}
			publisher.TopologyOutput = OutputInterface(&publisher.ElasticsearchOutput)
			logp.Info("Using Elasticsearch to store the topology")
		}
	}

	output, exists = outputs["redis"]
	if exists && output.Enabled && !publisher.disabled {
		logp.Debug("publish", "REDIS publisher enabled")
		err := publisher.RedisOutput.Init(output,
			agent.Topology_expire)
		if err != nil {
			logp.Err("Fail to initialize Redis as output: %s", err)
			return err
		}
		publisher.Output = append(publisher.Output, OutputInterface(&publisher.RedisOutput))

		if output.Save_topology {
			if publisher.TopologyOutput != nil {
				logp.Err("Multiple outputs defined to store topology. Please add save_topology = true option only for one output.")
				return errors.New("Multiple outputs defined to store topology")
			}
			publisher.TopologyOutput = OutputInterface(&publisher.RedisOutput)
			logp.Info("Using Redis to store the topology")
		}
	}

	output, exists = outputs["file"]
	if exists && output.Enabled {
		err := publisher.FileOutput.Init(output)
		if err != nil {
			logp.Err("Fail to initialize file output: %s", err)
			return err
		}
		publisher.Output = append(publisher.Output, OutputInterface(&publisher.FileOutput))

		// topology saving not supported by this one
	}

	if !publisher.disabled {
		if len(publisher.Output) == 0 {
			logp.Info("No outputs are defined. Please define one under [output]")
			return errors.New("No outputs are define")
		}

		if publisher.TopologyOutput == nil {
			logp.Warn("No output is defined to store the topology. The server fields might not be filled.")
		}
	}

	publisher.name = agent.Name
	if len(publisher.name) == 0 {
		// use the hostname
		publisher.name, err = os.Hostname()
		if err != nil {
			return err
		}

		logp.Info("No agent name configured, using hostname '%s'", publisher.name)
	}

	if len(agent.Tags) > 0 {
		publisher.tags = strings.Join(agent.Tags, " ")
	}

	if !publisher.disabled && publisher.TopologyOutput != nil {
		RefreshTopologyFreq := 10 * time.Second
		if agent.Refresh_topology_freq != 0 {
			RefreshTopologyFreq = time.Duration(agent.Refresh_topology_freq) * time.Second
		}
		publisher.RefreshTopologyTimer = time.Tick(RefreshTopologyFreq)
		logp.Info("Topology map refreshed every %s", RefreshTopologyFreq)

		// register agent and its public IP addresses
		err = publisher.PublishTopology()
		if err != nil {
			logp.Err("Failed to publish topology: %s", err)
			return err
		}

		// update topology periodically
		go publisher.UpdateTopologyPeriodically()
	}

	publisher.Queue = make(chan common.MapStr, 1000)
	go publisher.publishFromQueue()

	return nil
}
<commit_msg>Use a real array for tags. elastic/packetbeat#14<commit_after>package outputs

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/elastic/libbeat/common"
	"github.com/elastic/libbeat/logp"
)

type PublisherType struct {
	name                string
	tags                []string
	disabled            bool
	Index               string
	Output              []OutputInterface
	TopologyOutput      OutputInterface
	ElasticsearchOutput ElasticsearchOutputType
	RedisOutput         RedisOutputType
	FileOutput          FileOutputType
	IgnoreOutgoing      bool

	RefreshTopologyTimer <-chan time.Time
	Queue                chan common.MapStr
}

type MothershipConfig struct {
	Enabled            bool
	Save_topology      bool
	Host               string
	Port               int
	Protocol           string
	Username           string
	Password           string
	Index              string
	Path               string
	Db                 int
	Db_topology        int
	Timeout            int
	Reconnect_interval int
	Filename           string
	Rotate_every_kb    int
	Number_of_files    int
	DataType           string
	Flush_interval     int
}

type AgentConfig struct {
	Name                  string
	Refresh_topology_freq int
	Ignore_outgoing       bool
	Topology_expire       int
	Tags                  []string
}

var Publisher PublisherType

type Topology struct {
	Name string `json:"name"`
	Ip   string `json:"ip"`
}

func PrintPublishEvent(event common.MapStr) {
	json, err := json.MarshalIndent(event, "", "  ")
	if err != nil {
		logp.Err("json.Marshal: %s", err)
	} else {
		logp.Debug("publish", "Publish: %s", string(json))
	}
}

func (publisher *PublisherType) GetServerName(ip string) string {
	// in case the IP is localhost, return current agent name
	islocal, err := common.IsLoopback(ip)
	if err != nil {
		logp.Err("Parsing IP %s fails with: %s", ip, err)
		return ""
	} else {
		if islocal {
			return publisher.name
		}
	}
	// find the agent with the desired IP
	if publisher.TopologyOutput != nil {
		return publisher.TopologyOutput.GetNameByIP(ip)
	} else {
		return ""
	}
}

func (publisher *PublisherType) publishFromQueue() {
	for mapstr := range publisher.Queue {
		err := publisher.publishEvent(mapstr)
		if err != nil {
			logp.Err("Publishing failed: %v", err)
		}
	}
}

func (publisher *PublisherType) publishEvent(event common.MapStr) error {

	// the timestamp is mandatory
	ts, ok := event["timestamp"].(common.Time)
	if !ok {
		return errors.New("Missing 'timestamp' field from event.")
	}

	// the count is mandatory
	err := event.EnsureCountField()
	if err != nil {
		return err
	}

	// the type is mandatory
	_, ok = event["type"].(string)
	if !ok {
		return errors.New("Missing 'type' field from event.")
	}

	var src_server, dst_server string
	src, ok := event["src"].(*common.Endpoint)
	if ok {
		src_server = publisher.GetServerName(src.Ip)
		event["client_ip"] = src.Ip
		event["client_port"] = src.Port
		event["client_proc"] = src.Proc
		event["client_server"] = src_server
		delete(event, "src")
	}
	dst, ok := event["dst"].(*common.Endpoint)
	if ok {
		dst_server = publisher.GetServerName(dst.Ip)
		event["ip"] = dst.Ip
		event["port"] = dst.Port
		event["proc"] = dst.Proc
		event["server"] = dst_server
		delete(event, "dst")
	}

	if publisher.IgnoreOutgoing && dst_server != "" &&
		dst_server != publisher.name {
		// duplicated transaction -> ignore it
		logp.Debug("publish", "Ignore duplicated transaction on %s: %s -> %s", publisher.name, src_server, dst_server)
		return nil
	}

	event["agent"] = publisher.name
	if len(publisher.tags) > 0 {
		event["tags"] = publisher.tags
	}

	if _GeoLite != nil {
		real_ip, exists := event["real_ip"]
		if exists && len(real_ip.(string)) > 0 {
			loc := _GeoLite.GetLocationByIP(real_ip.(string))
			if loc != nil && loc.Latitude != 0 && loc.Longitude != 0 {
				event["client_location"] = fmt.Sprintf("%f, %f", loc.Latitude, loc.Longitude)
			}
		} else {
			if len(src_server) == 0 && src != nil { // only for external IP addresses
				loc := _GeoLite.GetLocationByIP(src.Ip)
				if loc != nil && loc.Latitude != 0 && loc.Longitude != 0 {
					event["client_location"] = fmt.Sprintf("%f, %f", loc.Latitude, loc.Longitude)
				}
			}
		}
	}

	if logp.IsDebug("publish") {
		PrintPublishEvent(event)
	}

	// add transaction
	has_error := false
	if !publisher.disabled {
		for i := 0; i < len(publisher.Output); i++ {
			err := publisher.Output[i].PublishEvent(time.Time(ts), event)
			if err != nil {
				logp.Err("Fail to publish event type on output %s: %s", publisher.Output, err)
				has_error = true
			}
		}
	}

	if has_error {
		return errors.New("Fail to publish event")
	}
	return nil
}

func (publisher *PublisherType) UpdateTopologyPeriodically() {
	for _ = range publisher.RefreshTopologyTimer {
		publisher.PublishTopology()
	}
}

func (publisher *PublisherType) PublishTopology(params ...string) error {

	var localAddrs []string = params

	if len(params) == 0 {
		addrs, err := common.LocalIpAddrsAsStrings(false)
		if err != nil {
			logp.Err("Getting local IP addresses fails with: %s", err)
			return err
		}
		localAddrs = addrs
	}

	if publisher.TopologyOutput != nil {
		logp.Debug("publish", "Add topology entry for %s: %s", publisher.name, localAddrs)

		err := publisher.TopologyOutput.PublishIPs(publisher.name, localAddrs)
		if err != nil {
			return err
		}
	}

	return nil
}

func (publisher *PublisherType) Init(publishDisabled bool,
	outputs map[string]MothershipConfig, agent AgentConfig) error {

	var err error
	publisher.IgnoreOutgoing = agent.Ignore_outgoing

	publisher.disabled = publishDisabled
	if publisher.disabled {
		logp.Info("Dry run mode. All output types except the file based one are disabled.")
	}

	output, exists := outputs["elasticsearch"]
	if exists && output.Enabled && !publisher.disabled {
		err := publisher.ElasticsearchOutput.Init(output,
			agent.Topology_expire)
		if err != nil {
			logp.Err("Fail to initialize Elasticsearch as output: %s", err)
			return err
		}
		publisher.Output = append(publisher.Output, OutputInterface(&publisher.ElasticsearchOutput))

		if output.Save_topology {
			if publisher.TopologyOutput != nil {
				logp.Err("Multiple outputs defined to store topology. Please add save_topology = true option only for one output.")
				return errors.New("Multiple outputs defined to store topology")
			}
			publisher.TopologyOutput = OutputInterface(&publisher.ElasticsearchOutput)
			logp.Info("Using Elasticsearch to store the topology")
		}
	}

	output, exists = outputs["redis"]
	if exists && output.Enabled && !publisher.disabled {
		logp.Debug("publish", "REDIS publisher enabled")
		err := publisher.RedisOutput.Init(output,
			agent.Topology_expire)
		if err != nil {
			logp.Err("Fail to initialize Redis as output: %s", err)
			return err
		}
		publisher.Output = append(publisher.Output, OutputInterface(&publisher.RedisOutput))

		if output.Save_topology {
			if publisher.TopologyOutput != nil {
				logp.Err("Multiple outputs defined to store topology. Please add save_topology = true option only for one output.")
				return errors.New("Multiple outputs defined to store topology")
			}
			publisher.TopologyOutput = OutputInterface(&publisher.RedisOutput)
			logp.Info("Using Redis to store the topology")
		}
	}

	output, exists = outputs["file"]
	if exists && output.Enabled {
		err := publisher.FileOutput.Init(output)
		if err != nil {
			logp.Err("Fail to initialize file output: %s", err)
			return err
		}
		publisher.Output = append(publisher.Output, OutputInterface(&publisher.FileOutput))

		// topology saving not supported by this one
	}

	if !publisher.disabled {
		if len(publisher.Output) == 0 {
			logp.Info("No outputs are defined. Please define one under [output]")
			return errors.New("No outputs are define")
		}

		if publisher.TopologyOutput == nil {
			logp.Warn("No output is defined to store the topology. The server fields might not be filled.")
		}
	}

	publisher.name = agent.Name
	if len(publisher.name) == 0 {
		// use the hostname
		publisher.name, err = os.Hostname()
		if err != nil {
			return err
		}

		logp.Info("No agent name configured, using hostname '%s'", publisher.name)
	}

	publisher.tags = agent.Tags

	if !publisher.disabled && publisher.TopologyOutput != nil {
		RefreshTopologyFreq := 10 * time.Second
		if agent.Refresh_topology_freq != 0 {
			RefreshTopologyFreq = time.Duration(agent.Refresh_topology_freq) * time.Second
		}
		publisher.RefreshTopologyTimer = time.Tick(RefreshTopologyFreq)
		logp.Info("Topology map refreshed every %s", RefreshTopologyFreq)

		// register agent and its public IP addresses
		err = publisher.PublishTopology()
		if err != nil {
			logp.Err("Failed to publish topology: %s", err)
			return err
		}

		// update topology periodically
		go publisher.UpdateTopologyPeriodically()
	}

	publisher.Queue = make(chan common.MapStr, 1000)
	go publisher.publishFromQueue()

	return nil
}
<|endoftext|>


<commit_before>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2016 Hajime Nakagami

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 firebirdsql

import (
	"database/sql"
	"testing"
)

func TestTransaction(t *testing.T) {
	var n int
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}

	// Connection (autocommit)
	conn.Exec("CREATE TABLE test_trans (s varchar(2048))")
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	err = conn.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 0 {
		t.Fatalf("Incorrect count: %v", n)
	}
	conn.Exec("INSERT INTO test_trans (s) values ('A')")
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	err = conn.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 1 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// Transaction
	tx, err := conn.Begin()
	if err != nil {
		t.Fatalf("Begin: %v", err)
	}

	// Rollback
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 1 {
		t.Fatalf("Incorrect count: %v", n)
	}
	_, err = tx.Exec("INSERT INTO test_trans (s) values ('B')")
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 2 {
		t.Fatalf("Incorrect count: %v", n)
	}
	err = tx.Rollback()
	if err != nil {
		t.Fatalf("Error Rollback: %v", err)
	}

	tx, err = conn.Begin()
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 1 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// Commit
	_, err = tx.Exec("INSERT INTO test_trans (s) values ('C')")
	err = tx.Commit()
	if err != nil {
		t.Fatalf("Error Commit: %v", err)
	}
	tx, err = conn.Begin()
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 2 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// without Commit (Need commit manually)
	_, err = tx.Exec("INSERT INTO test_trans (s) values ('D')")
	tx, err = conn.Begin()
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 2 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// Connection (autocommit)
	conn.Exec("INSERT INTO test_trans (s) values ('E')")
	conn.Close()
	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 3 {
		t.Fatalf("Incorrect count: %v", n)
	}

	conn.Close()
}

func TestIssue35(t *testing.T) {
	var n int
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test_issue35.fdb")

	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}

	tx, err := conn.Begin()

	if err != nil {
		t.Fatalf("Error Begin: %v", err)
	}

	err = tx.Commit()

	if err != nil {
		t.Fatalf("Error Commit: %v", err)
	}

	_, err = conn.Exec("CREATE TABLE test_issue35 (s varchar(2048))")

	if err != nil {
		t.Fatalf("Error CREATE TABLE: %v", err)
	}
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_issue35.fdb")
	err = conn.QueryRow("SELECT Count(*) FROM test_issue35").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 0 {
		t.Fatalf("Incorrect count: %v", n)
	}

	conn.Close()
}

func TestIssue38(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test_issue38.fdb")

	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	conn.Exec(`
        CREATE TABLE test_issue38 (
          id  INTEGER NOT NULL,
          key VARCHAR(64),
          value VARCHAR(64)
        )
    `)
	if err != nil {
		t.Fatalf("Error CREATE TABLE: %v", err)
	}
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_issue38.fdb")
	tx, err := conn.Begin()

	if err != nil {
		t.Fatalf("Error Begin: %v", err)
	}

	var rowId = sql.NullInt64{}

	err = tx.QueryRow(
		"INSERT INTO test_issue38 (id, key, value) VALUES (?, ?, ?) RETURNING id", 1, "testKey", "testValue").Scan(&rowId)
	if err == nil {
		t.Fatalf("'Dynamic SQL Error' is not occuerd.")
	}
	err = tx.Rollback()
	if err != nil {
		t.Fatalf("Error Rollback: %v", err)
	}

	conn.Close()
}

func TestIssue39(t *testing.T) {
	conn, err := sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	tx, err := conn.Begin()

	if err != nil {
		t.Fatalf("Error Begin: %v", err)
	}
	var rowId = sql.NullInt64{}
	err = tx.QueryRow("select 5 / 0 from rdb$database").Scan(&rowId)
	if err == nil {
		t.Fatalf("'Dynamic SQL Error' is not occured.")
	}
	err = tx.Rollback()
	if err != nil {
		t.Fatalf("broken transaction, but error is not occured.")
	}

	conn.Close()
}
<commit_msg>a bit modify test.<commit_after>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2016 Hajime Nakagami

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 firebirdsql

import (
	"database/sql"
	"testing"
)

func TestTransaction(t *testing.T) {
	var n int
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}

	// Connection (autocommit)
	conn.Exec("CREATE TABLE test_trans (s varchar(2048))")
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	err = conn.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 0 {
		t.Fatalf("Incorrect count: %v", n)
	}
	conn.Exec("INSERT INTO test_trans (s) values ('A')")
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	if err != nil {
		t.Fatalf("sql.Open(): %v", err)
	}
	err = conn.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 1 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// Transaction
	tx, err := conn.Begin()
	if err != nil {
		t.Fatalf("Begin: %v", err)
	}

	// Rollback
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 1 {
		t.Fatalf("Incorrect count: %v", n)
	}
	_, err = tx.Exec("INSERT INTO test_trans (s) values ('B')")
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 2 {
		t.Fatalf("Incorrect count: %v", n)
	}
	err = tx.Rollback()
	if err != nil {
		t.Fatalf("Error Rollback: %v", err)
	}

	tx, err = conn.Begin()
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 1 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// Commit
	_, err = tx.Exec("INSERT INTO test_trans (s) values ('C')")
	err = tx.Commit()
	if err != nil {
		t.Fatalf("Error Commit: %v", err)
	}
	tx, err = conn.Begin()
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 2 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// without Commit (Need commit manually)
	_, err = tx.Exec("INSERT INTO test_trans (s) values ('D')")
	tx, err = conn.Begin()
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 2 {
		t.Fatalf("Incorrect count: %v", n)
	}

	// Connection (autocommit)
	conn.Exec("INSERT INTO test_trans (s) values ('E')")
	conn.Close()
	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	err = tx.QueryRow("SELECT Count(*) FROM test_trans").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 3 {
		t.Fatalf("Incorrect count: %v", n)
	}

	conn.Close()
}

func TestIssue35(t *testing.T) {
	var n int
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test_issue35.fdb")

	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}

	tx, err := conn.Begin()

	if err != nil {
		t.Fatalf("Error Begin: %v", err)
	}

	err = tx.Commit()

	if err != nil {
		t.Fatalf("Error Commit: %v", err)
	}

	_, err = conn.Exec("CREATE TABLE test_issue35 (s varchar(2048))")

	if err != nil {
		t.Fatalf("Error CREATE TABLE: %v", err)
	}
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_issue35.fdb")
	err = conn.QueryRow("SELECT Count(*) FROM test_issue35").Scan(&n)
	if err != nil {
		t.Fatalf("Error SELECT: %v", err)
	}
	if n != 0 {
		t.Fatalf("Incorrect count: %v", n)
	}

	conn.Close()
}

func TestIssue38(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test_issue38.fdb")

	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	conn.Exec(`
        CREATE TABLE test_issue38 (
          id  INTEGER NOT NULL,
          key VARCHAR(64),
          value VARCHAR(64)
        )
    `)
	if err != nil {
		t.Fatalf("Error CREATE TABLE: %v", err)
	}
	conn.Close()

	conn, err = sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_issue38.fdb")
	tx, err := conn.Begin()

	if err != nil {
		t.Fatalf("Error Begin: %v", err)
	}

	var rowId = sql.NullInt64{}

	err = tx.QueryRow(
		"INSERT INTO test_issue38 (id, key, value) VALUES (?, ?, ?) RETURNING id", 1, "testKey", "testValue").Scan(&rowId)
	if err == nil {
		t.Fatalf("'Dynamic SQL Error' is not occuerd.")
	}
	err = tx.Rollback()
	if err != nil {
		t.Fatalf("Error Rollback: %v", err)
	}

	conn.Close()
}

func TestIssue39(t *testing.T) {
	conn, err := sql.Open("firebirdsql", "sysdba:masterkey@localhost:3050/tmp/go_test_transaction.fdb")
	tx, err := conn.Begin()

	if err != nil {
		t.Fatalf("Error Begin: %v", err)
	}
	var rowId = sql.NullInt64{}
	err = tx.QueryRow("select 5 / 0 from rdb$database").Scan(&rowId)
	if err == nil {
		t.Fatalf("'Dynamic SQL Error' is not occured.")
	}
	err = tx.Rollback()
	if err != nil {
		t.Fatalf("broken transaction, but error is not occured.")
	}

	conn.Close()
}
<|endoftext|>


<commit_before>package autoscale

import (
	"github.com/centurylinkcloud/clc-go-cli/base"
	"github.com/centurylinkcloud/clc-go-cli/models"
	"github.com/centurylinkcloud/clc-go-cli/models/server"
)

type SetOnServerReq struct {
	server.Server `argument:"composed" URIParam:"ServerId"`
	PolicyId      string `json:"Id"`
	PolicyName    string
}

type SetOnServerRes struct {
	Id    string
	Links []models.LinkEntity
}

func (p *SetOnServerReq) Validate() error {
	err := p.Server.Validate()
	if err != nil {
		return err
	}
	return validatePolicy(p.PolicyId, p.PolicyName)
}

func (p *SetOnServerReq) InferID(cn base.Connection) error {
	err := p.Server.InferID(cn)
	if err != nil {
		return err
	}
	id, err := inferID(p.PolicyName, cn)
	if err == nil && id != "" {
		p.PolicyId = id
	}
	return err
}

func (p *SetOnServerReq) GetNames(cn base.Connection, property string) ([]string, error) {
	if property != "ServerName" {
		return p.Server.GetNames(cn, property)
	}
	return getNames(cn, property)
}
<commit_msg>but in set on server command fixed<commit_after>package autoscale

import (
	"github.com/centurylinkcloud/clc-go-cli/base"
	"github.com/centurylinkcloud/clc-go-cli/models"
	"github.com/centurylinkcloud/clc-go-cli/models/server"
)

type SetOnServerReq struct {
	server.Server `argument:"composed" URIParam:"ServerId"`
	PolicyId      string `json:"Id"`
	PolicyName    string
}

type SetOnServerRes struct {
	Id    string
	Links []models.LinkEntity
}

func (p *SetOnServerReq) Validate() error {
	err := p.Server.Validate()
	if err != nil {
		return err
	}
	return validatePolicy(p.PolicyId, p.PolicyName)
}

func (p *SetOnServerReq) InferID(cn base.Connection) error {
	err := p.Server.InferID(cn)
	if err != nil {
		return err
	}
	id, err := inferID(p.PolicyName, cn)
	if err == nil && id != "" {
		p.PolicyId = id
	}
	return err
}

func (p *SetOnServerReq) GetNames(cn base.Connection, property string) ([]string, error) {
	if property == "ServerName" {
		return p.Server.GetNames(cn, property)
	}
	return getNames(cn, property)
}
<|endoftext|>


<commit_before>package nextwedding

import (
	"fmt"
	"github.com/gamelost/bot3server/server"
	"time"
)

type NextWeddingService struct{}

func (svc *NextWeddingService) NewService() server.BotHandler {
	return &NextWeddingService{}
}

func (svc *NextWeddingService) Handle(botRequest *server.BotRequest, botResponse *server.BotResponse) {

	botResponse.SetSingleLineResponse(durationToWeddingDate())
}

func durationToWeddingDate() string {

	nowDate := time.Now()
	loc, _ := time.LoadLocation("America/Los_Angeles")
	weddingDate := time.Date(2014, time.September, 6, 16, 0, 0, 0, loc)

	if nowDate.After(weddingDate) {
		return "Ah mon. It be too late, the deed is done!"
	} else {

		duration := weddingDate.Sub(nowDate)
		durationInMinutes := int(duration.Minutes())
		durationDays := durationInMinutes / (24 * 60)
		durationHours := (durationInMinutes - (durationDays * 60 * 24)) / 60
		durationMinutes := durationInMinutes - ((durationDays * 60 * 24) + (durationHours * 60))

		return fmt.Sprintf("Only %d days, %d hours and %d minutes left for natech to come to his senses. Flee while you can, FLEEEEEE!", durationDays, durationHours, durationMinutes)
	}
}
<commit_msg>damn natech moving around wedding dates<commit_after>package nextwedding

import (
	"fmt"
	"github.com/gamelost/bot3server/server"
	"time"
)

type NextWeddingService struct{}

func (svc *NextWeddingService) NewService() server.BotHandler {
	return &NextWeddingService{}
}

func (svc *NextWeddingService) Handle(botRequest *server.BotRequest, botResponse *server.BotResponse) {

	botResponse.SetSingleLineResponse(durationToWeddingDate())
}

func durationToWeddingDate() string {

	nowDate := time.Now()
	loc, _ := time.LoadLocation("America/Los_Angeles")
	weddingDate := time.Date(2014, time.September, 20, 16, 0, 0, 0, loc)

	if nowDate.After(weddingDate) {
		return "Ah mon. It be too late, the deed is done!"
	} else {

		duration := weddingDate.Sub(nowDate)
		durationInMinutes := int(duration.Minutes())
		durationDays := durationInMinutes / (24 * 60)
		durationHours := (durationInMinutes - (durationDays * 60 * 24)) / 60
		durationMinutes := durationInMinutes - ((durationDays * 60 * 24) + (durationHours * 60))

		return fmt.Sprintf("Only %d days, %d hours and %d minutes left for natech to come to his senses. Flee while you can, FLEEEEEE!", durationDays, durationHours, durationMinutes)
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package controllers

import (
	"net/http"

	"github.com/dgrijalva/jwt-go"
	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
	"github.com/labstack/echo"
)

// Secret : TODO
var Secret string

// AuthenticatedUser : Get the authenticated user from the JWT Token
func AuthenticatedUser(c echo.Context) models.User {
	var u models.User

	user := c.Get("user").(*jwt.Token)

	claims, ok := user.Claims.(jwt.MapClaims)
	if ok {
		u.Username = claims["username"].(string)
		u.Admin = claims["admin"].(bool)
	}

	return u
}

// AuthenticateHandler manages user authentication
func AuthenticateHandler(c echo.Context) error {
	u := &models.User{}

	err := c.Bind(u)
	if err != nil {
		return echo.NewHTTPError(400, err.Error())
	}

	err = u.Validate()
	if err != nil {
		h.L.Error(err.Error())
		return echo.NewHTTPError(400, err.Error())
	}

	res, err := u.Authenticate()
	if err != nil {
		h.L.Error(err.Error())
		return echo.NewHTTPError(400, err.Error())
	}

	if !res.OK {
		h.L.Error(res.Message + " (" + u.Username + ")")
		return echo.NewHTTPError(403, res.Message)
	}

	if err := h.ValidCliVersion(c.Request()); err != nil {
		h.L.Error(err.Error())
		return echo.NewHTTPError(403, err.Error())
	}

	return c.JSON(http.StatusOK, map[string]string{"token": res.Token})
}
<commit_msg>Clean up syntax<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package controllers

import (
	"net/http"

	"github.com/dgrijalva/jwt-go"
	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
	"github.com/labstack/echo"
)

// Secret : TODO
var Secret string

// AuthenticatedUser : Get the authenticated user from the JWT Token
func AuthenticatedUser(c echo.Context) models.User {
	var u models.User

	user := c.Get("user").(*jwt.Token)

	claims, ok := user.Claims.(jwt.MapClaims)
	if ok {
		u.Username = claims["username"].(string)
		u.Admin = claims["admin"].(bool)
	}

	return u
}

// AuthenticateHandler manages user authentication
func AuthenticateHandler(c echo.Context) error {
	var u models.User

	err := c.Bind(&u)
	if err != nil {
		return echo.NewHTTPError(400, err.Error())
	}

	err = u.Validate()
	if err != nil {
		h.L.Error(err.Error())
		return echo.NewHTTPError(400, err.Error())
	}

	res, err := u.Authenticate()
	if err != nil {
		h.L.Error(err.Error())
		return echo.NewHTTPError(400, err.Error())
	}

	if !res.OK {
		h.L.Error(res.Message + " (" + u.Username + ")")
		return echo.NewHTTPError(403, res.Message)
	}

	if err := h.ValidCliVersion(c.Request()); err != nil {
		h.L.Error(err.Error())
		return echo.NewHTTPError(403, err.Error())
	}

	return c.JSON(http.StatusOK, map[string]string{"token": res.Token})
}
<|endoftext|>


<commit_before>package load

import (
	"errors"
	"fmt"

	"github.com/gansoi/gansoi/plugins"
	"github.com/gansoi/gansoi/transports"
)

type (
	// Load reads load and process data from Linux hosts.
	Load struct{}
)

var (
	// ErrSyntax will be returned, if we don't understand the format of /proc/loadavg.
	ErrSyntax = errors.New("Unknown format of /proc/loadavg")
)

func init() {
	plugins.RegisterAgent("linuxload", Load{})
}

// RemoteCheck implements RemoteAgent.
func (l *Load) RemoteCheck(transport transports.Transport, result plugins.AgentResult) error {
	contents, err := transport.ReadFile("/proc/loadavg")
	if err != nil {
		return err
	}

	var load1 float32
	var load5 float32
	var load15 float32
	var running int
	var total int
	var lastPid int

	_, err = fmt.Sscanf(string(contents), "%f %f %f %d/%d %d", &load1, &load5, &load15, &running, &total, &lastPid)

	if err != nil {
		return ErrSyntax
	}

	result.AddValue("Load1", load1)
	result.AddValue("Load5", load5)
	result.AddValue("Load15", load15)
	result.AddValue("Running", running)
	result.AddValue("Processes", total)

	return nil
}
<commit_msg>Changed linuxload to use float64<commit_after>package load

import (
	"errors"
	"fmt"

	"github.com/gansoi/gansoi/plugins"
	"github.com/gansoi/gansoi/transports"
)

type (
	// Load reads load and process data from Linux hosts.
	Load struct{}
)

var (
	// ErrSyntax will be returned, if we don't understand the format of /proc/loadavg.
	ErrSyntax = errors.New("Unknown format of /proc/loadavg")
)

func init() {
	plugins.RegisterAgent("linuxload", Load{})
}

// RemoteCheck implements RemoteAgent.
func (l *Load) RemoteCheck(transport transports.Transport, result plugins.AgentResult) error {
	contents, err := transport.ReadFile("/proc/loadavg")
	if err != nil {
		return err
	}

	var load1 float64
	var load5 float64
	var load15 float64
	var running int
	var total int
	var lastPid int

	_, err = fmt.Sscanf(string(contents), "%f %f %f %d/%d %d", &load1, &load5, &load15, &running, &total, &lastPid)

	if err != nil {
		return ErrSyntax
	}

	result.AddValue("Load1", load1)
	result.AddValue("Load5", load5)
	result.AddValue("Load15", load15)
	result.AddValue("Running", running)
	result.AddValue("Processes", total)

	return nil
}
<|endoftext|>