<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 contiv defines flavor used for Contiv-VPP agent.
package contiv

import (
	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/flavors/local"

	"github.com/contiv/vpp/flavors/ksr"
	"github.com/contiv/vpp/plugins/contiv"
	"github.com/contiv/vpp/plugins/kvdbproxy"
	"github.com/contiv/vpp/plugins/policy"
	"github.com/contiv/vpp/plugins/service"
	"github.com/contiv/vpp/plugins/statscollector"
	"github.com/golang/protobuf/proto"
	"github.com/ligato/cn-infra/config"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/kvdbsync"
	local_sync "github.com/ligato/cn-infra/datasync/kvdbsync/local"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/db/keyval/etcdv3"
	"github.com/ligato/cn-infra/flavors/connectors"
	"github.com/ligato/cn-infra/health/probe"
	"github.com/ligato/cn-infra/rpc/grpc"
	"github.com/ligato/cn-infra/rpc/prometheus"
	"github.com/ligato/cn-infra/rpc/rest"
	"github.com/ligato/cn-infra/servicelabel"
	"github.com/ligato/vpp-agent/clientv1/linux/localclient"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/linuxplugin"
)

const (
	// ContivConfigPath is the default location of Agent's Contiv plugin. This path reflects configuration in k8s/contiv-vpp.yaml.
	ContivConfigPath = "/etc/agent/contiv.yaml"

	// ContivConfigPathUsage explains the purpose of 'kube-config' flag.
	ContivConfigPathUsage = "Path to the Agent's Contiv plugin configuration yaml file."
)

// NewAgent returns a new instance of the Agent with plugins.
// It is an alias for core.NewAgent() to implicit use of the FlavorContiv
func NewAgent(opts ...core.Option) *core.Agent {
	return core.NewAgent(&FlavorContiv{}, opts...)
}

// WithPlugins for adding custom plugins to SFC Controller
// <listPlugins> is a callback that uses flavor input to
// inject dependencies for custom plugins that are in output
func WithPlugins(listPlugins func(local *FlavorContiv) []*core.NamedPlugin) core.WithPluginsOpt {
	return &withPluginsOpt{listPlugins}
}

// FlavorContiv glues together multiple plugins to manage VPP and Linux
// configuration using the local client.
type FlavorContiv struct {
	*local.FlavorLocal
	HTTP       rest.Plugin
	HealthRPC  probe.Plugin
	Prometheus prometheus.Plugin

	ETCD            etcdv3.Plugin
	ETCDDataSync    kvdbsync.Plugin
	NodeIDDataSync  kvdbsync.Plugin
	ServiceDataSync kvdbsync.Plugin
	PolicyDataSync  kvdbsync.Plugin

	KVProxy kvdbproxy.Plugin
	Stats   statscollector.Plugin

	LinuxLocalClient localclient.Plugin
	GoVPP            govppmux.GOVPPPlugin
	Linux            linuxplugin.Plugin
	VPP              defaultplugins.Plugin
	GRPC             grpc.Plugin
	Contiv           contiv.Plugin
	Policy           policy.Plugin
	Service          service.Plugin

	// resync should the last plugin in the flavor in order to give
	// the others enough time to register
	ResyncOrch resync.Plugin

	injected bool
}

// Inject sets inter-plugin references.
func (f *FlavorContiv) Inject() bool {
	if f.injected {
		return false
	}
	f.injected = true

	if f.FlavorLocal == nil {
		f.FlavorLocal = &local.FlavorLocal{}
	}
	f.FlavorLocal.Inject()

	rest.DeclareHTTPPortFlag("http")
	httpPlugDeps := *f.InfraDeps("http", local.WithConf())
	f.HTTP.Deps.Log = httpPlugDeps.Log
	f.HTTP.Deps.PluginConfig = httpPlugDeps.PluginConfig
	f.HTTP.Deps.PluginName = httpPlugDeps.PluginName

	f.Prometheus.Deps.PluginInfraDeps = *f.InfraDeps("prometheus")
	f.Prometheus.Deps.HTTP = &f.HTTP

	f.Logs.HTTP = &f.HTTP

	f.HealthRPC.Deps.PluginInfraDeps = *f.InfraDeps("health-rpc")
	f.HealthRPC.Deps.HTTP = &f.HTTP
	f.HealthRPC.Deps.StatusCheck = &f.StatusCheck

	f.ETCD.Deps.PluginInfraDeps = *f.InfraDeps("etcdv3", local.WithConf())
	connectors.InjectKVDBSync(&f.ETCDDataSync, &f.ETCD, f.ETCD.PluginName, f.FlavorLocal, &f.ResyncOrch)
	f.NodeIDDataSync = f.ETCDDataSync
	f.NodeIDDataSync.PluginInfraDeps = *f.InfraDeps("nodeid-datasync")
	f.NodeIDDataSync.Deps.PluginInfraDeps.ServiceLabel = servicelabel.OfDifferentAgent(ksr.MicroserviceLabel)
	f.PolicyDataSync = f.ETCDDataSync
	f.PolicyDataSync.PluginInfraDeps = *f.InfraDeps("policy-datasync")
	f.PolicyDataSync.Deps.PluginInfraDeps.ServiceLabel = servicelabel.OfDifferentAgent(ksr.MicroserviceLabel)
	f.ServiceDataSync = f.ETCDDataSync
	f.ServiceDataSync.PluginInfraDeps = *f.InfraDeps("service-datasync")
	f.ServiceDataSync.Deps.PluginInfraDeps.ServiceLabel = servicelabel.OfDifferentAgent(ksr.MicroserviceLabel)

	f.KVProxy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("kvproxy")
	f.KVProxy.Deps.KVDB = &f.ETCDDataSync

	f.Stats.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("stats")
	f.Stats.Deps.Contiv = &f.Contiv
	f.Stats.Deps.Prometheus = &f.Prometheus

	f.GoVPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("govpp", local.WithConf())
	f.Linux.Watcher = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{&f.KVProxy, local_sync.Get()}}
	f.Linux.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("linuxplugin", local.WithConf())

	f.VPP.Watch = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{&f.KVProxy, local_sync.Get()}}
	f.VPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("default-plugins", local.WithConf())
	f.VPP.Deps.Linux = &f.Linux
	f.VPP.Deps.GoVppmux = &f.GoVPP
	f.VPP.Deps.PublishStatistics = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&f.Stats}}
	f.VPP.Deps.IfStatePub = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&devNullWriter{}}}

	grpc.DeclareGRPCPortFlag("grpc")
	grpcInfraDeps := f.FlavorLocal.InfraDeps("grpc", local.WithConf())
	f.GRPC.Deps.Log = grpcInfraDeps.Log
	f.GRPC.Deps.PluginName = grpcInfraDeps.PluginName
	f.GRPC.Deps.PluginConfig = grpcInfraDeps.PluginConfig

	f.Contiv.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("cni-grpc")
	f.Contiv.Deps.GRPC = &f.GRPC
	f.Contiv.Deps.Proxy = &f.KVProxy
	f.Contiv.Deps.GoVPP = &f.GoVPP
	f.Contiv.Deps.VPP = &f.VPP
	f.Contiv.Deps.Resync = &f.ResyncOrch
	f.Contiv.Deps.ETCD = &f.ETCD
	f.Contiv.Deps.Watcher = &f.NodeIDDataSync
	f.Contiv.Deps.PluginConfig = config.ForPlugin("contiv", ContivConfigPath, ContivConfigPathUsage)

	f.Policy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("policy")
	f.Policy.Deps.Resync = &f.ResyncOrch
	f.Policy.Deps.Watcher = &f.PolicyDataSync
	f.Policy.Deps.Contiv = &f.Contiv
	f.Policy.Deps.GoVPP = &f.GoVPP
	f.Policy.Deps.VPP = &f.VPP

	f.Service.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("service")
	f.Service.Deps.Resync = &f.ResyncOrch
	f.Service.Deps.Watcher = &f.ServiceDataSync
	f.Service.Deps.Contiv = &f.Contiv
	f.Service.Deps.VPP = &f.VPP

	f.ResyncOrch.PluginLogDeps = *f.LogDeps("resync-orch")

	return true
}

// Plugins combines all Plugins in the flavor to a list.
func (f *FlavorContiv) Plugins() []*core.NamedPlugin {
	f.Inject()
	return core.ListPluginsInFlavor(f)
}

type devNullWriter struct {
}

func (d *devNullWriter) Put(key string, data proto.Message, opts ...datasync.PutOption) error {
	return nil
}

// withPluginsOpt is return value of vppLocal.WithPlugins() utility
// to easily define new plugins for the agent based on FlavorContiv.
type withPluginsOpt struct {
	callback func(local *FlavorContiv) []*core.NamedPlugin
}

// OptionMarkerCore is just for marking implementation that it implements this interface
func (opt *withPluginsOpt) OptionMarkerCore() {}

// Plugins methods is here to implement core.WithPluginsOpt go interface
// <flavor> is a callback that uses flavor input for dependency injection
// for custom plugins (returned as NamedPlugin)
func (opt *withPluginsOpt) Plugins(flavors ...core.Flavor) []*core.NamedPlugin {
	for _, flavor := range flavors {
		if f, ok := flavor.(*FlavorContiv); ok {
			return opt.callback(f)
		}
	}

	panic("wrong usage of contiv.WithPlugin() for other than FlavorContiv")
}
<commit_msg>Add sync mutex to flavor<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 contiv defines flavor used for Contiv-VPP agent.
package contiv

import (
	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/flavors/local"

	"github.com/contiv/vpp/flavors/ksr"
	"github.com/contiv/vpp/plugins/contiv"
	"github.com/contiv/vpp/plugins/kvdbproxy"
	"github.com/contiv/vpp/plugins/policy"
	"github.com/contiv/vpp/plugins/service"
	"github.com/contiv/vpp/plugins/statscollector"
	"github.com/golang/protobuf/proto"
	"github.com/ligato/cn-infra/config"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/kvdbsync"
	local_sync "github.com/ligato/cn-infra/datasync/kvdbsync/local"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/db/keyval/etcdv3"
	"github.com/ligato/cn-infra/flavors/connectors"
	"github.com/ligato/cn-infra/health/probe"
	"github.com/ligato/cn-infra/rpc/grpc"
	"github.com/ligato/cn-infra/rpc/prometheus"
	"github.com/ligato/cn-infra/rpc/rest"
	"github.com/ligato/cn-infra/servicelabel"
	"github.com/ligato/vpp-agent/clientv1/linux/localclient"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/linuxplugin"
	"sync"
)

const (
	// ContivConfigPath is the default location of Agent's Contiv plugin. This path reflects configuration in k8s/contiv-vpp.yaml.
	ContivConfigPath = "/etc/agent/contiv.yaml"

	// ContivConfigPathUsage explains the purpose of 'kube-config' flag.
	ContivConfigPathUsage = "Path to the Agent's Contiv plugin configuration yaml file."
)

// NewAgent returns a new instance of the Agent with plugins.
// It is an alias for core.NewAgent() to implicit use of the FlavorContiv
func NewAgent(opts ...core.Option) *core.Agent {
	return core.NewAgent(&FlavorContiv{}, opts...)
}

// WithPlugins for adding custom plugins to SFC Controller
// <listPlugins> is a callback that uses flavor input to
// inject dependencies for custom plugins that are in output
func WithPlugins(listPlugins func(local *FlavorContiv) []*core.NamedPlugin) core.WithPluginsOpt {
	return &withPluginsOpt{listPlugins}
}

// FlavorContiv glues together multiple plugins to manage VPP and Linux
// configuration using the local client.
type FlavorContiv struct {
	*local.FlavorLocal
	HTTP       rest.Plugin
	HealthRPC  probe.Plugin
	Prometheus prometheus.Plugin

	ETCD            etcdv3.Plugin
	ETCDDataSync    kvdbsync.Plugin
	NodeIDDataSync  kvdbsync.Plugin
	ServiceDataSync kvdbsync.Plugin
	PolicyDataSync  kvdbsync.Plugin

	KVProxy kvdbproxy.Plugin
	Stats   statscollector.Plugin

	LinuxLocalClient localclient.Plugin
	GoVPP            govppmux.GOVPPPlugin
	Linux            linuxplugin.Plugin
	VPP              defaultplugins.Plugin
	GRPC             grpc.Plugin
	Contiv           contiv.Plugin
	Policy           policy.Plugin
	Service          service.Plugin

	// resync should the last plugin in the flavor in order to give
	// the others enough time to register
	ResyncOrch resync.Plugin

	injected bool
}

// Inject sets inter-plugin references.
func (f *FlavorContiv) Inject() bool {
	if f.injected {
		return false
	}
	f.injected = true

	if f.FlavorLocal == nil {
		f.FlavorLocal = &local.FlavorLocal{}
	}
	f.FlavorLocal.Inject()

	rest.DeclareHTTPPortFlag("http")
	httpPlugDeps := *f.InfraDeps("http", local.WithConf())
	f.HTTP.Deps.Log = httpPlugDeps.Log
	f.HTTP.Deps.PluginConfig = httpPlugDeps.PluginConfig
	f.HTTP.Deps.PluginName = httpPlugDeps.PluginName

	f.Prometheus.Deps.PluginInfraDeps = *f.InfraDeps("prometheus")
	f.Prometheus.Deps.HTTP = &f.HTTP

	f.Logs.HTTP = &f.HTTP

	f.HealthRPC.Deps.PluginInfraDeps = *f.InfraDeps("health-rpc")
	f.HealthRPC.Deps.HTTP = &f.HTTP
	f.HealthRPC.Deps.StatusCheck = &f.StatusCheck

	f.ETCD.Deps.PluginInfraDeps = *f.InfraDeps("etcdv3", local.WithConf())
	connectors.InjectKVDBSync(&f.ETCDDataSync, &f.ETCD, f.ETCD.PluginName, f.FlavorLocal, &f.ResyncOrch)
	f.NodeIDDataSync = f.ETCDDataSync
	f.NodeIDDataSync.PluginInfraDeps = *f.InfraDeps("nodeid-datasync")
	f.NodeIDDataSync.Deps.PluginInfraDeps.ServiceLabel = servicelabel.OfDifferentAgent(ksr.MicroserviceLabel)
	f.PolicyDataSync = f.ETCDDataSync
	f.PolicyDataSync.PluginInfraDeps = *f.InfraDeps("policy-datasync")
	f.PolicyDataSync.Deps.PluginInfraDeps.ServiceLabel = servicelabel.OfDifferentAgent(ksr.MicroserviceLabel)
	f.ServiceDataSync = f.ETCDDataSync
	f.ServiceDataSync.PluginInfraDeps = *f.InfraDeps("service-datasync")
	f.ServiceDataSync.Deps.PluginInfraDeps.ServiceLabel = servicelabel.OfDifferentAgent(ksr.MicroserviceLabel)

	f.KVProxy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("kvproxy")
	f.KVProxy.Deps.KVDB = &f.ETCDDataSync

	f.Stats.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("stats")
	f.Stats.Deps.Contiv = &f.Contiv
	f.Stats.Deps.Prometheus = &f.Prometheus

	// Mutex for synchronizing watching events
	var watchEventsMutex sync.Mutex

	f.GoVPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("govpp", local.WithConf())
	f.Linux.Watcher = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{&f.KVProxy, local_sync.Get()}}
	f.Linux.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("linuxplugin", local.WithConf())
	f.Linux.Deps.WatchEventsMutex = &watchEventsMutex

	f.VPP.Watch = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{&f.KVProxy, local_sync.Get()}}
	f.VPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("default-plugins", local.WithConf())
	f.VPP.Deps.Linux = &f.Linux
	f.VPP.Deps.GoVppmux = &f.GoVPP
	f.VPP.Deps.PublishStatistics = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&f.Stats}}
	f.VPP.Deps.IfStatePub = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&devNullWriter{}}}
	f.VPP.Deps.WatchEventsMutex = &watchEventsMutex

	grpc.DeclareGRPCPortFlag("grpc")
	grpcInfraDeps := f.FlavorLocal.InfraDeps("grpc", local.WithConf())
	f.GRPC.Deps.Log = grpcInfraDeps.Log
	f.GRPC.Deps.PluginName = grpcInfraDeps.PluginName
	f.GRPC.Deps.PluginConfig = grpcInfraDeps.PluginConfig

	f.Contiv.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("cni-grpc")
	f.Contiv.Deps.GRPC = &f.GRPC
	f.Contiv.Deps.Proxy = &f.KVProxy
	f.Contiv.Deps.GoVPP = &f.GoVPP
	f.Contiv.Deps.VPP = &f.VPP
	f.Contiv.Deps.Resync = &f.ResyncOrch
	f.Contiv.Deps.ETCD = &f.ETCD
	f.Contiv.Deps.Watcher = &f.NodeIDDataSync
	f.Contiv.Deps.PluginConfig = config.ForPlugin("contiv", ContivConfigPath, ContivConfigPathUsage)

	f.Policy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("policy")
	f.Policy.Deps.Resync = &f.ResyncOrch
	f.Policy.Deps.Watcher = &f.PolicyDataSync
	f.Policy.Deps.Contiv = &f.Contiv
	f.Policy.Deps.GoVPP = &f.GoVPP
	f.Policy.Deps.VPP = &f.VPP

	f.Service.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("service")
	f.Service.Deps.Resync = &f.ResyncOrch
	f.Service.Deps.Watcher = &f.ServiceDataSync
	f.Service.Deps.Contiv = &f.Contiv
	f.Service.Deps.VPP = &f.VPP

	f.ResyncOrch.PluginLogDeps = *f.LogDeps("resync-orch")

	return true
}

// Plugins combines all Plugins in the flavor to a list.
func (f *FlavorContiv) Plugins() []*core.NamedPlugin {
	f.Inject()
	return core.ListPluginsInFlavor(f)
}

type devNullWriter struct {
}

func (d *devNullWriter) Put(key string, data proto.Message, opts ...datasync.PutOption) error {
	return nil
}

// withPluginsOpt is return value of vppLocal.WithPlugins() utility
// to easily define new plugins for the agent based on FlavorContiv.
type withPluginsOpt struct {
	callback func(local *FlavorContiv) []*core.NamedPlugin
}

// OptionMarkerCore is just for marking implementation that it implements this interface
func (opt *withPluginsOpt) OptionMarkerCore() {}

// Plugins methods is here to implement core.WithPluginsOpt go interface
// <flavor> is a callback that uses flavor input for dependency injection
// for custom plugins (returned as NamedPlugin)
func (opt *withPluginsOpt) Plugins(flavors ...core.Flavor) []*core.NamedPlugin {
	for _, flavor := range flavors {
		if f, ok := flavor.(*FlavorContiv); ok {
			return opt.callback(f)
		}
	}

	panic("wrong usage of contiv.WithPlugin() for other than FlavorContiv")
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"gopkg.in/sorcix/irc.v2"
	"strings"
)

type Client struct {
	Tls     bool
	Details string
	Nick    string
	User    string
	Conn    *irc.Conn
}

func New(usetls bool, details, nick, user string) (*Client, error) {
	var ret Client
	var conn *irc.Conn
	var tconn *tls.Conn
	var err error
	if usetls {
		tconn, err = tls.Dial("tcp", details, &tls.Config{})
		conn = irc.NewConn(tconn)
	} else {
		conn, err = irc.Dial(details)
	}
	ret = Client{usetls, details, nick, user, conn}
	return &ret, err
}

func (c Client) Send(msg *irc.Message) {
	c.Conn.Encode(msg)
}

func (c Client) Receive() (*irc.Message, error) {
	msg, err := c.Conn.Decode()
	if msg.Command == "PING" {
		pong := fmt.Sprintf("PONG :%s", msg.Params[0])
		c.Conn.Encode(irc.ParseMessage(pong))
	} else if msg.Command == "PRIVMSG" && msg.Params[1][0] == '\x01' {
		msg.Command = "CTCP"
		msg.Params[1] = strings.Replace(msg.Params[1], "\x01", "", -1)
		if msg.Params[1] == "VERSION" {
			reply := fmt.Sprintf("NOTICE %s :%s", msg.Prefix.Name, "\x01VERSION Bunnyirc (https://github.com/japanoise/bunnyirc)\x01")
			c.Conn.Encode(irc.ParseMessage(reply))
		}
	} else if msg.Command == "NOTICE" && msg.Params[1][0] == '\x01' {
		msg.Command = "CTCPREPLY"
		msg.Params[1] = strings.Replace(msg.Params[1], "\x01", "", -1)
	}
	return msg, err
}

func (c Client) Close() {
	c.Close()
}

func (c Client) Auth() {
	for {
		msg, _ := c.Conn.Decode()
		if msg.Command == "NOTICE" {
			c.Conn.Encode(irc.ParseMessage(fmt.Sprintf("NICK %s", c.Nick)))
			c.Conn.Encode(irc.ParseMessage(fmt.Sprintf("USER %s * * :%s", c.User, c.User)))
			return
		}
	}
}
<commit_msg>responds to CTCP PING<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"gopkg.in/sorcix/irc.v2"
	"strings"
)

type Client struct {
	Tls     bool
	Details string
	Nick    string
	User    string
	Conn    *irc.Conn
}

func New(usetls bool, details, nick, user string) (*Client, error) {
	var ret Client
	var conn *irc.Conn
	var tconn *tls.Conn
	var err error
	if usetls {
		tconn, err = tls.Dial("tcp", details, &tls.Config{})
		conn = irc.NewConn(tconn)
	} else {
		conn, err = irc.Dial(details)
	}
	ret = Client{usetls, details, nick, user, conn}
	return &ret, err
}

func (c Client) Send(msg *irc.Message) {
	c.Conn.Encode(msg)
}

func (c Client) Receive() (*irc.Message, error) {
	msg, err := c.Conn.Decode()
	if msg.Command == "PING" {
		pong := fmt.Sprintf("PONG :%s", msg.Params[0])
		c.Conn.Encode(irc.ParseMessage(pong))
	} else if msg.Command == "PRIVMSG" && msg.Params[1][0] == '\x01' {
		msg.Command = "CTCP"
		msg.Params[1] = strings.Replace(msg.Params[1], "\x01", "", -1)
		if msg.Params[1] == "VERSION" {
			reply := fmt.Sprintf("NOTICE %s :%s", msg.Prefix.Name, "\x01VERSION Bunnyirc (https://github.com/japanoise/bunnyirc)\x01")
			c.Conn.Encode(irc.ParseMessage(reply))
		} else if strings.HasPrefix(msg.Params[1], "PING") {
			reply := fmt.Sprintf("NOTICE %s :\x01%s\x01", msg.Prefix.Name, msg.Params[1])
			c.Conn.Encode(irc.ParseMessage(reply))
		}
	} else if msg.Command == "NOTICE" && msg.Params[1][0] == '\x01' {
		msg.Command = "CTCPREPLY"
		msg.Params[1] = strings.Replace(msg.Params[1], "\x01", "", -1)
	}
	return msg, err
}

func (c Client) Close() {
	c.Close()
}

func (c Client) Auth() {
	for {
		msg, _ := c.Conn.Decode()
		if msg.Command == "NOTICE" {
			c.Conn.Encode(irc.ParseMessage(fmt.Sprintf("NICK %s", c.Nick)))
			c.Conn.Encode(irc.ParseMessage(fmt.Sprintf("USER %s * * :%s", c.User, c.User)))
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2020-2022 Buf Technologies, 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,
// 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 testingextended

import (
	"flag"
	"testing"
	"time"
)

// SkipIfShort skips the test if testing.short is set.
func SkipIfShort(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode")
	}
}

// GetTestTimeout returns the value of the go test -timeout flag.
func GetTestTimeout(t *testing.T) time.Duration {
	if !flag.Parsed() {
		t.Fatal("unable to read testing timeout flag as flags have not been parsed")
	}
	// It's fine if this panics. We expect to be in a test, and this should
	// be covered by the Go 1 compatibility promise.
	return flag.Lookup("test.timeout").Value.(flag.Getter).Get().(time.Duration)
}
<commit_msg>Set a default timeout in GetTestTimeout (#857)<commit_after>// Copyright 2020-2022 Buf Technologies, 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,
// 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 testingextended

import (
	"testing"
	"time"
)

// SkipIfShort skips the test if testing.short is set.
func SkipIfShort(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode")
	}
}

// GetTestTimeout returns the time remaining until the test times out or 10m if the test is not set to timeout.
func GetTestTimeout(t *testing.T) time.Duration {
	if deadline, ok := t.Deadline(); ok && !deadline.IsZero() {
		return time.Until(deadline)
	}
	return 10 * time.Minute
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"
	"strings"
)

func put(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/profile", "/ob/profile/":
		i.PUTProfile(w, r)
	case "/ob/settings", "/ob/settings/":
		i.PUTSettings(w, r)
	case "/ob/moderator", "/ob/moderator/":
		i.PUTModerator(w, r)
	case "/ob/listing", "/ob/listing/":
		i.PUTListing(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func post(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/listing", "/ob/listing/":
		i.POSTListing(w, r)
	case "/ob/purchase", "/ob/purchase/":
		i.POSTPurchase(w, r)
	case "/ob/follow", "/ob/follow/":
		i.POSTFollow(w, r)
	case "/ob/unfollow", "/ob/unfollow/":
		i.POSTUnfollow(w, r)
	case "/ob/profile", "/ob/profile/":
		i.POSTProfile(w, r)
	case "/ob/images", "/ob/images/":
		i.POSTImage(w, r)
	case "/wallet/spend", "/wallet/spend/":
		i.POSTSpendCoins(w, r)
	case "/ob/settings", "/ob/settings/":
		i.POSTSettings(w, r)
	case "/ob/inventory", "/ob/inventory/":
		i.POSTInventory(w, r)
	case "/ob/moderator", "/ob/moderator/":
		i.POSTModerator(w, r)
	case "/ob/avatar", "/ob/avatar/":
		i.POSTAvatar(w, r)
	case "/ob/header", "/ob/header/":
		i.POSTHeader(w, r)
	case "/ob/orderconfirmation", "/ob/orderconfirmation/":
		i.POSTOrderConfirmation(w, r)
	case "/ob/ordercancel", "/ob/ordercancel/":
		i.POSTOrderCancel(w, r)
	case "/ob/orderfulfillment", "/ob/orderfulfillment/":
		i.POSTOrderFulfill(w, r)
	case "/ob/ordercompletion", "/ob/ordercompletion/":
		i.POSTOrderComplete(w, r)
	case "/ob/refund", "/ob/refund/":
		i.POSTRefund(w, r)
	case "/wallet/resyncblockchain", "/wallet/resyncblockchain/":
		i.POSTResyncBlockchain(w, r)
	case "/ob/shutdown", "/ob/shutdown/":
		i.POSTShutdown(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func get(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/status/"):
		i.GETStatus(w, r)
	case strings.Contains(path, "/ob/peers"):
		i.GETPeers(w, r)
	case strings.Contains(path, "/ob/config"):
		i.GETConfig(w, r)
	case strings.Contains(path, "/wallet/address"):
		i.GETAddress(w, r)
	case strings.Contains(path, "/wallet/mnemonic"):
		i.GETMnemonic(w, r)
	case strings.Contains(path, "/wallet/balance"):
		i.GETBalance(w, r)
	case strings.Contains(path, "/ob/settings"):
		i.GETSettings(w, r)
	case strings.Contains(path, "/ob/closestpeers"):
		i.GETClosestPeers(w, r)
	case strings.Contains(path, "/ob/exchangerate"):
		i.GETExchangeRate(w, r)
	case strings.Contains(path, "/ob/followers"):
		i.GETFollowers(w, r)
	case strings.Contains(path, "/ob/following"):
		i.GETFollowing(w, r)
	case strings.Contains(path, "/ob/inventory"):
		i.GETInventory(w, r)
	case strings.Contains(path, "/ob/listings"):
		i.GETListings(w, r)
	case strings.Contains(path, "/ob/listing"):
		i.GETListing(w, r)
	case strings.Contains(path, "/ob/followsme"):
		i.GETFollowsMe(w, r)
	case strings.Contains(path, "/ob/isfollowing"):
		i.GETIsFollowing(w, r)
	case strings.Contains(path, "/ob/order"):
		i.GETOrder(w, r)
	case strings.Contains(path, "/ob/moderators"):
		i.GETModerators(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func patch(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/settings", "/ob/settings/":
		i.PATCHSettings(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func deleter(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/moderator", "/ob/moderator/":
		i.DELETEModerator(w, r)
	case "/ob/listing", "/ob/listing/":
		i.DELETEListing(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}
<commit_msg>BUGFIX: Fix /ob/status endpoint to tolerate no trailing /.<commit_after>package api

import (
	"net/http"
	"strings"
)

func put(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/profile", "/ob/profile/":
		i.PUTProfile(w, r)
	case "/ob/settings", "/ob/settings/":
		i.PUTSettings(w, r)
	case "/ob/moderator", "/ob/moderator/":
		i.PUTModerator(w, r)
	case "/ob/listing", "/ob/listing/":
		i.PUTListing(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func post(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/listing", "/ob/listing/":
		i.POSTListing(w, r)
	case "/ob/purchase", "/ob/purchase/":
		i.POSTPurchase(w, r)
	case "/ob/follow", "/ob/follow/":
		i.POSTFollow(w, r)
	case "/ob/unfollow", "/ob/unfollow/":
		i.POSTUnfollow(w, r)
	case "/ob/profile", "/ob/profile/":
		i.POSTProfile(w, r)
	case "/ob/images", "/ob/images/":
		i.POSTImage(w, r)
	case "/wallet/spend", "/wallet/spend/":
		i.POSTSpendCoins(w, r)
	case "/ob/settings", "/ob/settings/":
		i.POSTSettings(w, r)
	case "/ob/inventory", "/ob/inventory/":
		i.POSTInventory(w, r)
	case "/ob/moderator", "/ob/moderator/":
		i.POSTModerator(w, r)
	case "/ob/avatar", "/ob/avatar/":
		i.POSTAvatar(w, r)
	case "/ob/header", "/ob/header/":
		i.POSTHeader(w, r)
	case "/ob/orderconfirmation", "/ob/orderconfirmation/":
		i.POSTOrderConfirmation(w, r)
	case "/ob/ordercancel", "/ob/ordercancel/":
		i.POSTOrderCancel(w, r)
	case "/ob/orderfulfillment", "/ob/orderfulfillment/":
		i.POSTOrderFulfill(w, r)
	case "/ob/ordercompletion", "/ob/ordercompletion/":
		i.POSTOrderComplete(w, r)
	case "/ob/refund", "/ob/refund/":
		i.POSTRefund(w, r)
	case "/wallet/resyncblockchain", "/wallet/resyncblockchain/":
		i.POSTResyncBlockchain(w, r)
	case "/ob/shutdown", "/ob/shutdown/":
		i.POSTShutdown(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func get(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/status"):
		i.GETStatus(w, r)
	case strings.Contains(path, "/ob/peers"):
		i.GETPeers(w, r)
	case strings.Contains(path, "/ob/config"):
		i.GETConfig(w, r)
	case strings.Contains(path, "/wallet/address"):
		i.GETAddress(w, r)
	case strings.Contains(path, "/wallet/mnemonic"):
		i.GETMnemonic(w, r)
	case strings.Contains(path, "/wallet/balance"):
		i.GETBalance(w, r)
	case strings.Contains(path, "/ob/settings"):
		i.GETSettings(w, r)
	case strings.Contains(path, "/ob/closestpeers"):
		i.GETClosestPeers(w, r)
	case strings.Contains(path, "/ob/exchangerate"):
		i.GETExchangeRate(w, r)
	case strings.Contains(path, "/ob/followers"):
		i.GETFollowers(w, r)
	case strings.Contains(path, "/ob/following"):
		i.GETFollowing(w, r)
	case strings.Contains(path, "/ob/inventory"):
		i.GETInventory(w, r)
	case strings.Contains(path, "/ob/listings"):
		i.GETListings(w, r)
	case strings.Contains(path, "/ob/listing"):
		i.GETListing(w, r)
	case strings.Contains(path, "/ob/followsme"):
		i.GETFollowsMe(w, r)
	case strings.Contains(path, "/ob/isfollowing"):
		i.GETIsFollowing(w, r)
	case strings.Contains(path, "/ob/order"):
		i.GETOrder(w, r)
	case strings.Contains(path, "/ob/moderators"):
		i.GETModerators(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func patch(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/settings", "/ob/settings/":
		i.PATCHSettings(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func deleter(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch path {
	case "/ob/moderator", "/ob/moderator/":
		i.DELETEModerator(w, r)
	case "/ob/listing", "/ob/listing/":
		i.DELETEListing(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}
<|endoftext|>


<commit_before>package fftw

// #cgo pkg-config: fftw3
import "C"
<commit_msg>Revert to old LDFLAGS for pull request<commit_after>package fftw

// #cgo LDFLAGS: -lfftw3 -lm
import "C"
<|endoftext|>


<commit_before>package TF2RconWrapper

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
)

type RawMessage struct {
	data []byte
	n    int //length
}

func (r RawMessage) String() string {
	return string(r.data)
}

// RconChatListener maintains an UDP server that receives redirected chat messages from TF2 servers
type RconChatListener struct {
	conn        *net.UDPConn
	servers     map[string]*ServerListener
	serversLock *sync.RWMutex
	addr        *net.UDPAddr
	localip     string
	port        string
	rng         *rand.Rand
}

// NewRconChatListener builds a new RconChatListener. Its arguments are localip (the ip of this server) and
// port (the port the listener will use)
func NewRconChatListener(localip, port string) (*RconChatListener, error) {
	addr, err := net.ResolveUDPAddr("udp4", ":"+port)
	if err != nil {
		return nil, err
	}

	servers := make(map[string]*ServerListener)

	rng := rand.New(rand.NewSource(time.Now().Unix()))

	listener := &RconChatListener{nil, servers, new(sync.RWMutex), addr, localip, port, rng}
	listener.startListening()
	return listener, nil
}

func (r *RconChatListener) startListening() {
	conn, err := net.ListenUDP("udp", r.addr)
	r.conn = conn
	if err != nil {
		log.Fatal(err)
		return
	}
	conn.SetReadBuffer(1048576)
	go r.readStrings()
}

func (r *RconChatListener) readStrings() {
	rawMessageC := make(chan RawMessage, 500)

	go func() {
		for {
			raw := <-rawMessageC
			message := raw.data[0:raw.n]
			secret, err := getSecret(message)
			if err != nil {
				continue
			}

			r.serversLock.RLock()
			s, ok := r.servers[secret]
			r.serversLock.RUnlock()
			if ok {
				s.RawMessages <- raw
			}
		}
	}()

	for {
		buff := make([]byte, 65000)
		n, err := r.conn.Read(buff)
		//log.Println(string(buff[0:n]))

		if err != nil {
			fmt.Println("Error receiving server chat data: ", err)
			continue
		}

		rawMessageC <- RawMessage{buff, n}
	}
}

// Close stops the RconChatListener
func (s *ServerListener) Close(m *TF2RconConnection) {
	s.listener.serversLock.Lock()
	delete(s.listener.servers, s.secret)
	s.listener.serversLock.Unlock()

	m.StopLogRedirection(s.listener.localip, s.listener.port)
}

// CreateServerListener creates a ServerListener that receives chat messages from a
// particular TF2 server
func (r *RconChatListener) CreateServerListener(m *TF2RconConnection) *ServerListener {

	secret := strconv.Itoa(r.rng.Intn(999998) + 1)

	r.serversLock.RLock()
	_, ok := r.servers[secret]
	for ok {
		secret = strconv.Itoa(r.rng.Intn(999998) + 1)
		_, ok = r.servers[secret]
	}
	r.serversLock.RUnlock()

	s := &ServerListener{make(chan RawMessage), m.host, secret, r}

	r.serversLock.Lock()
	r.servers[secret] = s
	//log.Println(r.servers)
	r.serversLock.Unlock()

	m.Query("sv_logsecret " + secret)
	m.RedirectLogs(r.localip, r.port)
	return s
}

// ServerListener represents a listener that receives chat messages from a particular
// TF2 server. It's built and managed by an RconChatListener instance.
type ServerListener struct {
	RawMessages chan RawMessage

	host     string
	secret   string
	listener *RconChatListener
}
<commit_msg>Add buffer to RawMessage channel, use waitgroup<commit_after>package TF2RconWrapper

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"time"
)

type RawMessage struct {
	data []byte
	n    int //length
}

func (r RawMessage) String() string {
	return string(r.data)
}

// RconChatListener maintains an UDP server that receives redirected chat messages from TF2 servers
type RconChatListener struct {
	conn        *net.UDPConn
	servers     map[string]*ServerListener
	serversLock *sync.RWMutex
	wait        *sync.WaitGroup
	addr        *net.UDPAddr
	localip     string
	port        string
	rng         *rand.Rand
}

// NewRconChatListener builds a new RconChatListener. Its arguments are localip (the ip of this server) and
// port (the port the listener will use)
func NewRconChatListener(localip, port string) (*RconChatListener, error) {
	addr, err := net.ResolveUDPAddr("udp4", ":"+port)
	if err != nil {
		return nil, err
	}

	servers := make(map[string]*ServerListener)

	rng := rand.New(rand.NewSource(time.Now().Unix()))

	listener := &RconChatListener{nil, servers, new(sync.RWMutex), new(sync.WaitGroup), addr, localip, port, rng}
	listener.startListening()
	return listener, nil
}

func (r *RconChatListener) startListening() {
	conn, err := net.ListenUDP("udp", r.addr)
	r.conn = conn
	if err != nil {
		log.Fatal(err)
		return
	}
	conn.SetReadBuffer(1048576)
	go r.readStrings()
}

func (r *RconChatListener) readStrings() {
	rawMessageC := make(chan RawMessage, 500)

	go func() {
		for {
			raw := <-rawMessageC
			r.wait.Wait()
			message := raw.data[0:raw.n]
			secret, err := getSecret(message)
			if err != nil {
				continue
			}

			r.serversLock.RLock()
			s, ok := r.servers[secret]
			r.serversLock.RUnlock()
			if ok {
				s.RawMessages <- raw
			}
		}
	}()

	for {
		buff := make([]byte, 65000)
		n, err := r.conn.Read(buff)
		//log.Println(string(buff[0:n]))

		if err != nil {
			fmt.Println("Error receiving server chat data: ", err)
			continue
		}

		rawMessageC <- RawMessage{buff, n}
	}
}

// Close stops the RconChatListener
func (s *ServerListener) Close(m *TF2RconConnection) {
	s.listener.wait.Add(1)
	delete(s.listener.servers, s.secret)
	s.listener.wait.Done()

	m.StopLogRedirection(s.listener.localip, s.listener.port)
}

// CreateServerListener creates a ServerListener that receives chat messages from a
// particular TF2 server
func (r *RconChatListener) CreateServerListener(m *TF2RconConnection) *ServerListener {

	secret := strconv.Itoa(r.rng.Intn(999998) + 1)

	r.serversLock.RLock()
	_, ok := r.servers[secret]
	for ok {
		secret = strconv.Itoa(r.rng.Intn(999998) + 1)
		_, ok = r.servers[secret]
	}
	r.serversLock.RUnlock()

	s := &ServerListener{make(chan RawMessage, 10), m.host, secret, r}

	r.serversLock.Lock()
	r.servers[secret] = s
	//log.Println(r.servers)
	r.serversLock.Unlock()

	m.Query("sv_logsecret " + secret)
	m.RedirectLogs(r.localip, r.port)
	return s
}

// ServerListener represents a listener that receives chat messages from a particular
// TF2 server. It's built and managed by an RconChatListener instance.
type ServerListener struct {
	RawMessages chan RawMessage

	host     string
	secret   string
	listener *RconChatListener
}
<|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) << 18) * 1000, 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 timestamp meaningful<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>// Copyright 2016 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package harvey

import (
	"crypto/sha1"
	"fmt"
	"io"
	"path/filepath"

	"bldy.build/build"
	"bldy.build/build/project"
)

type ObjCopy struct {
	Name         string   `objcopy:"name"`
	Dependencies []string `objcopy:"deps"`
	In           string   `objcopy:"infile"`
	Out          string   `objcopy:"outfile"`
}

func (oc *ObjCopy) GetName() string {
	return oc.Name
}

func (oc *ObjCopy) GetDependencies() []string {
	return oc.Dependencies
}

func (oc *ObjCopy) Hash() []byte {
	h := sha1.New()
	io.WriteString(h, oc.In)
	io.WriteString(h, oc.Out)
	io.WriteString(h, oc.Name)
	return []byte{}
}

// Had to be done
func Copier() string {
	if tpfx := project.Getenv("TOOLPREFIX"); tpfx == "" {
		return "objcopy"
	} else {
		return fmt.Sprintf("%s%s", tpfx, "objcopy")
	}
}
func (oc *ObjCopy) Build(c *build.Context) error {
	params := []string{}
	params = append(params, "-I")
	params = append(params, oc.In)
	params = append(params, "-O")
	params = append(params, oc.Out)
	params = append(params, filepath.Join("bin", split(oc.Dependencies[0], ":")))
	params = append(params, oc.Name)
	if err := c.Exec(Copier(), nil, params); err != nil {
		return fmt.Errorf(err.Error())
	}
	return nil
}
func (oc *ObjCopy) Installs() map[string]string {
	installs := make(map[string]string)
	installs[filepath.Join("bin", oc.Name)] = oc.Name
	return installs
}
<commit_msg>objcopy: don't move things to bin<commit_after>// Copyright 2016 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package harvey

import (
	"crypto/sha1"
	"fmt"
	"io"
	"path/filepath"

	"bldy.build/build"
	"bldy.build/build/project"
)

type ObjCopy struct {
	Name         string   `objcopy:"name"`
	Dependencies []string `objcopy:"deps"`
	In           string   `objcopy:"infile"`
	Out          string   `objcopy:"outfile"`
}

func (oc *ObjCopy) GetName() string {
	return oc.Name
}

func (oc *ObjCopy) GetDependencies() []string {
	return oc.Dependencies
}

func (oc *ObjCopy) Hash() []byte {
	h := sha1.New()
	io.WriteString(h, oc.In)
	io.WriteString(h, oc.Out)
	io.WriteString(h, oc.Name)
	return []byte{}
}

// Had to be done
func Copier() string {
	if tpfx := project.Getenv("TOOLPREFIX"); tpfx == "" {
		return "objcopy"
	} else {
		return fmt.Sprintf("%s%s", tpfx, "objcopy")
	}
}
func (oc *ObjCopy) Build(c *build.Context) error {
	params := []string{}
	params = append(params, "-I")
	params = append(params, oc.In)
	params = append(params, "-O")
	params = append(params, oc.Out)
	params = append(params, filepath.Join("bin", split(oc.Dependencies[0], ":")))
	params = append(params, oc.Name)
	if err := c.Exec(Copier(), nil, params); err != nil {
		return fmt.Errorf(err.Error())
	}
	return nil
}
func (oc *ObjCopy) Installs() map[string]string {
	installs := make(map[string]string)
	installs[oc.Name] = oc.Name
	return installs
}
<|endoftext|>


<commit_before><commit_msg>Call UpdateShipCount() instead of GetShipCount()<commit_after><|endoftext|>


<commit_before><commit_msg>Add function to write ethernet payloads<commit_after><|endoftext|>


<commit_before>package redisocket

import (
	"time"

	"github.com/garyburd/redigo/redis"
)

type eventPayload struct {
	payload *Payload
	event   string
}

type Pool struct {
	users         map[*Client]bool
	broadcast     chan *eventPayload
	join          chan *Client
	leave         chan *Client
	rpool         *redis.Pool
	channelPrefix string
}

func (h *Pool) Run() {
	t := time.NewTicker(30 * time.Second)
	defer func() {
		t.Stop()
	}()
	for {
		select {
		case p := <-h.broadcast:
			for u, _ := range h.users {
				u.Trigger(p.event, p.payload)
			}

		case u := <-h.join:
			h.users[u] = true

		case u := <-h.leave:
			if _, ok := h.users[u]; ok {
				close(u.send)
				delete(h.users, u)
			}
		case <-t.C:
			conn := h.rpool.Get()
			conn.Send("MULTI")
			for u, _ := range h.users {
				if u.uid != "" {
					conn.Send("SADD", h.channelPrefix+u.prefix+"@"+"online", u.uid)
				}
				for e, _ := range u.events {
					conn.Send("SADD", h.channelPrefix+u.prefix+"@"+"channels:"+e, u.uid)
					conn.Send("EXPIRE", h.channelPrefix+u.prefix+"@"+"channels:"+e, 35)
				}
			}
			conn.Send("EXPIRE", h.channelPrefix+"online", 35)
			conn.Do("EXEC")
			conn.Close()
		}

	}
}
func (a *Pool) Broadcast(event string, p *Payload) {
	a.broadcast <- &eventPayload{p, event}
}
func (a *Pool) Join(c *Client) {
	a.join <- c
}
func (a *Pool) Leave(c *Client) {
	a.leave <- c
}
<commit_msg>update vendor add 30s del key<commit_after>package redisocket

import (
	"time"

	"github.com/garyburd/redigo/redis"
)

type eventPayload struct {
	payload *Payload
	event   string
}

type Pool struct {
	users         map[*Client]bool
	broadcast     chan *eventPayload
	join          chan *Client
	leave         chan *Client
	rpool         *redis.Pool
	channelPrefix string
}

func (h *Pool) Run() {
	t := time.NewTicker(30 * time.Second)
	defer func() {
		t.Stop()
	}()
	for {
		select {
		case p := <-h.broadcast:
			for u, _ := range h.users {
				u.Trigger(p.event, p.payload)
			}

		case u := <-h.join:
			h.users[u] = true

		case u := <-h.leave:
			if _, ok := h.users[u]; ok {
				close(u.send)
				delete(h.users, u)
			}
		case <-t.C:
			conn := h.rpool.Get()
			conn.Send("MULTI")
			conn.Send("DEL", h.channelPrefix+u.prefix+"@"+"online")
			conn.Send("DEL", h.channelPrefix+u.prefix+"@"+"channels:"+e)
			for u, _ := range h.users {
				if u.uid != "" {
					conn.Send("SADD", h.channelPrefix+u.prefix+"@"+"online", u.uid)
				}
				for e, _ := range u.events {
					conn.Send("SADD", h.channelPrefix+u.prefix+"@"+"channels:"+e, u.uid)
					conn.Send("EXPIRE", h.channelPrefix+u.prefix+"@"+"channels:"+e, 35)
				}
			}
			conn.Send("EXPIRE", h.channelPrefix+"online", 35)
			conn.Do("EXEC")
			conn.Close()
		}

	}
}
func (a *Pool) Broadcast(event string, p *Payload) {
	a.broadcast <- &eventPayload{p, event}
}
func (a *Pool) Join(c *Client) {
	a.join <- c
}
func (a *Pool) Leave(c *Client) {
	a.leave <- c
}
<|endoftext|>


<commit_before>package chronos

import (
	"os"
	"testing"
	"time"
)

func TestMain(m *testing.M) {
	os.Exit(m.Run())
}

func TestOnceAtDateSchedule(t *testing.T) {
	now := time.Now()
	plan := NewOnceAtDatePlan(now.Add(time.Duration(3) * time.Second))

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(5) * time.Second)
	if executionCount != 1 {
		t.Fatalf("expected execution count to be 1 but was %d", executionCount)
	}
}

func TestOnceAfterDuration(t *testing.T) {
	plan := NewOnceAfterDuration(time.Duration(4) * time.Second)

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(6) * time.Second)
	if executionCount != 1 {
		t.Fatalf("expected execution count to be 1 but was %d", executionCount)
	}
}

func TestDailySchedule(t *testing.T) {
	now := time.Now()
	plan := NewDailySchedulingPlan([]DayTime{
		{now.Hour(), now.Minute(), now.Second() + 4},
		{now.Hour(), now.Minute(), now.Second() + 2},
		{now.Hour(), now.Minute(), now.Second() + 1},
	})

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(6) * time.Second)
	if executionCount != 3 {
		t.Fatalf("expected execution count to be 3 but was %d", executionCount)
	}
}

func TestWeeklySchedule(t *testing.T) {
	now := time.Now()
	plan := NewWeeklySchedulingPlan([]Weekday{
		{day: now.Weekday(), at: DayTime{now.Hour(), now.Minute(), now.Second() + 4}},
		{day: now.Weekday(), at: DayTime{now.Hour(), now.Minute(), now.Second() + 2}},
		{day: now.Weekday(), at: DayTime{now.Hour(), now.Minute(), now.Second() + 1}},
	})

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(6) * time.Second)
	if executionCount != 3 {
		t.Fatalf("expected execution count to be 3 but was %d", executionCount)
	}
}

func TestMonthlySchedule(t *testing.T) {
	now := time.Now()
	plan := NewMonthlySchedulingPlan([]MonthDay{
		{day: uint(now.Day()), at: DayTime{now.Hour(), now.Minute(), now.Second() + 4}},
		{day: uint(now.Day()), at: DayTime{now.Hour(), now.Minute(), now.Second() + 2}},
		{day: uint(now.Day()), at: DayTime{now.Hour(), now.Minute(), now.Second() + 1}},
	})

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(6) * time.Second)
	if executionCount != 3 {
		t.Fatalf("expected execution count to be 3 but was %d", executionCount)
	}
}
<commit_msg>run tests in parallel<commit_after>package chronos

import (
	"os"
	"testing"
	"time"
)

func TestMain(m *testing.M) {
	os.Exit(m.Run())
}

func TestOnceAtDateSchedule(t *testing.T) {
	t.Parallel()
	now := time.Now()
	plan := NewOnceAtDatePlan(now.Add(time.Duration(2) * time.Second))

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(3) * time.Second)
	if executionCount != 1 {
		t.Fatalf("expected execution count to be 1 but was %d", executionCount)
	}
}

func TestOnceAfterDuration(t *testing.T) {
	t.Parallel()
	plan := NewOnceAfterDuration(time.Duration(2) * time.Second)

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(3) * time.Second)
	if executionCount != 1 {
		t.Fatalf("expected execution count to be 1 but was %d", executionCount)
	}
}

func TestDailySchedule(t *testing.T) {
	t.Parallel()
	now := time.Now()
	plan := NewDailySchedulingPlan([]DayTime{
		{now.Hour(), now.Minute(), now.Second() + 3},
		{now.Hour(), now.Minute(), now.Second() + 2},
		{now.Hour(), now.Minute(), now.Second() + 1},
	})

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(4) * time.Second)
	if executionCount != 3 {
		t.Fatalf("expected execution count to be 3 but was %d", executionCount)
	}
}

func TestWeeklySchedule(t *testing.T) {
	t.Parallel()
	now := time.Now()
	plan := NewWeeklySchedulingPlan([]Weekday{
		{day: now.Weekday(), at: DayTime{now.Hour(), now.Minute(), now.Second() + 3}},
		{day: now.Weekday(), at: DayTime{now.Hour(), now.Minute(), now.Second() + 2}},
		{day: now.Weekday(), at: DayTime{now.Hour(), now.Minute(), now.Second() + 1}},
	})

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(4) * time.Second)
	if executionCount != 3 {
		t.Fatalf("expected execution count to be 3 but was %d", executionCount)
	}
}

func TestMonthlySchedule(t *testing.T) {
	t.Parallel()
	now := time.Now()
	plan := NewMonthlySchedulingPlan([]MonthDay{
		{day: uint(now.Day()), at: DayTime{now.Hour(), now.Minute(), now.Second() + 3}},
		{day: uint(now.Day()), at: DayTime{now.Hour(), now.Minute(), now.Second() + 2}},
		{day: uint(now.Day()), at: DayTime{now.Hour(), now.Minute(), now.Second() + 1}},
	})

	executionCount := 0
	task := NewScheduledTask(func() {
		executionCount++
	}, plan)
	defer task.Stop()
	task.Start()
	<-time.After(time.Duration(4) * time.Second)
	if executionCount != 3 {
		t.Fatalf("expected execution count to be 3 but was %d", executionCount)
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strconv"
	"time"
)

// API channels.list: Lists all channels in a Slack team.
func (sl *Slack) ChannelsList() ([]*Channel, error) {
	uv := sl.urlValues()
	body, err := sl.GetRequest(channelsListApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(ChannelsListAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res.Channels()
}

// response type for `channels.list` api
type ChannelsListAPIResponse struct {
	BaseAPIResponse
	RawChannels json.RawMessage `json:"channels"`
}

// slack channel type
type Channel struct {
	Id         string          `json:"id"`
	Name       string          `json:"name"`
	IsChannel  bool            `json:"is_channel"`
	Created    int             `json:"created"`
	Creator    string          `json:"creator"`
	IsArchived bool            `json:"is_archived"`
	IsGeneral  bool            `json:"is_general"`
	IsMember   bool            `json:"is_member"`
	Members    []string        `json:"members"`
	RawTopic   json.RawMessage `json:"topic"`
	RawPurpose json.RawMessage `json:"purpose"`
	NumMembers int             `json:"num_members"`
}

// Channels returns a slice of channel object from a response of `channels.list` api.
func (res *ChannelsListAPIResponse) Channels() ([]*Channel, error) {
	var chs []*Channel
	err := json.Unmarshal(res.RawChannels, &chs)
	if err != nil {
		return nil, err
	}
	return chs, nil
}

func (ch *Channel) Topic() (*Topic, error) {
	tp := new(Topic)
	err := json.Unmarshal(ch.RawTopic, tp)
	if err != nil {
		return nil, err
	}
	return tp, nil
}

func (ch *Channel) Purpose() (*Purpose, error) {
	pp := new(Purpose)
	err := json.Unmarshal(ch.RawPurpose, pp)
	if err != nil {
		return nil, err
	}
	return pp, nil
}

// FindChannel returns a channel object that satisfy conditions specified.
func (sl *Slack) FindChannel(cb func(*Channel) bool) (*Channel, error) {
	channels, err := sl.ChannelsList()
	if err != nil {
		return nil, err
	}
	for _, channel := range channels {
		if cb(channel) {
			return channel, nil
		}
	}
	return nil, errors.New("No such channel.")
}

// FindChannelByName returns a channel object that matches name specified.
func (sl *Slack) FindChannelByName(name string) (*Channel, error) {
	return sl.FindChannel(func(channel *Channel) bool {
		return channel.Name == name
	})
}

// API channels.join: Joins a channel, creating it if needed.
func (sl *Slack) JoinChannel(name string) error {
	uv := sl.urlValues()
	uv.Add("name", name)

	_, err := sl.GetRequest(channelsJoinApiEndpoint, uv)
	if err != nil {
		return err
	}
	return nil
}

type Message struct {
	Type   string `json:"type"`
	Ts     string `json:"ts"`
	UserId string `json:"user"`
	Text   string `json:"text"`
}

func (msg *Message) Timestamp() *time.Time {
	tsf, _ := strconv.ParseFloat(msg.Ts, 64)
	ts := time.Unix(int64(tsf), 0)
	return &ts
}

// option type for `channels.history` api
type ChannelsHistoryOpt struct {
	Channel   string  `json:"channel"`
	Latest    float64 `json:"latest"`
	Oldest    float64 `json:"oldest"`
	Inclusive int     `json:"inclusive"`
	Count     int     `json:"count"`
	UnReads   int     `json:"unreads,omitempty"`
}

func (opt *ChannelsHistoryOpt) Bind(uv *url.Values) error {
	uv.Add("channel", opt.Channel)
	if opt.Latest != 0.0 {
		uv.Add("lastest", strconv.FormatFloat(opt.Latest, 'f', 6, 64))
	}
	if opt.Oldest != 0.0 {
		uv.Add("oldest", strconv.FormatFloat(opt.Oldest, 'f', 6, 64))
	}
	uv.Add("inclusive", strconv.Itoa(opt.Inclusive))
	if opt.Count != 0 {
		uv.Add("count", strconv.Itoa(opt.Count))
	}
	uv.Add("unreads", strconv.Itoa(opt.UnReads))
	return nil
}

// response type for `channels.history` api
type ChannelsHistoryResponse struct {
	BaseAPIResponse
	Latest             float64    `json:"latest"`
	Messages           []*Message `json:"messages"`
	HasMore            bool       `json:"has_more"`
	UnReadCountDisplay int        `json:"unread_count_display"`
}

// API channels.history: Fetches history of messages and events from a channel.
func (sl *Slack) ChannelsHistory(opt *ChannelsHistoryOpt) (*ChannelsHistoryResponse, error) {
	uv := sl.urlValues()
	err := opt.Bind(uv)
	if err != nil {
		return nil, err
	}
	body, err := sl.GetRequest(channelsHistoryApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(ChannelsHistoryResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res, nil
}

func (sl *Slack) ChannelsHistoryMessages(opt *ChannelsHistoryOpt) ([]*Message, error) {
	res, err := sl.ChannelsHistory(opt)
	if err != nil {
		return nil, err
	}
	return res.Messages, nil
}
<commit_msg>Add Subtype to the Message struct. Ratio: there are more than 20 different message subtypes (https://api.slack.com/events/message). Introducing this field makes filtering possible, i.e. simple message could be filtered with msg.Subtype == ""<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strconv"
	"time"
)

// API channels.list: Lists all channels in a Slack team.
func (sl *Slack) ChannelsList() ([]*Channel, error) {
	uv := sl.urlValues()
	body, err := sl.GetRequest(channelsListApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(ChannelsListAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res.Channels()
}

// response type for `channels.list` api
type ChannelsListAPIResponse struct {
	BaseAPIResponse
	RawChannels json.RawMessage `json:"channels"`
}

// slack channel type
type Channel struct {
	Id         string          `json:"id"`
	Name       string          `json:"name"`
	IsChannel  bool            `json:"is_channel"`
	Created    int             `json:"created"`
	Creator    string          `json:"creator"`
	IsArchived bool            `json:"is_archived"`
	IsGeneral  bool            `json:"is_general"`
	IsMember   bool            `json:"is_member"`
	Members    []string        `json:"members"`
	RawTopic   json.RawMessage `json:"topic"`
	RawPurpose json.RawMessage `json:"purpose"`
	NumMembers int             `json:"num_members"`
}

// Channels returns a slice of channel object from a response of `channels.list` api.
func (res *ChannelsListAPIResponse) Channels() ([]*Channel, error) {
	var chs []*Channel
	err := json.Unmarshal(res.RawChannels, &chs)
	if err != nil {
		return nil, err
	}
	return chs, nil
}

func (ch *Channel) Topic() (*Topic, error) {
	tp := new(Topic)
	err := json.Unmarshal(ch.RawTopic, tp)
	if err != nil {
		return nil, err
	}
	return tp, nil
}

func (ch *Channel) Purpose() (*Purpose, error) {
	pp := new(Purpose)
	err := json.Unmarshal(ch.RawPurpose, pp)
	if err != nil {
		return nil, err
	}
	return pp, nil
}

// FindChannel returns a channel object that satisfy conditions specified.
func (sl *Slack) FindChannel(cb func(*Channel) bool) (*Channel, error) {
	channels, err := sl.ChannelsList()
	if err != nil {
		return nil, err
	}
	for _, channel := range channels {
		if cb(channel) {
			return channel, nil
		}
	}
	return nil, errors.New("No such channel.")
}

// FindChannelByName returns a channel object that matches name specified.
func (sl *Slack) FindChannelByName(name string) (*Channel, error) {
	return sl.FindChannel(func(channel *Channel) bool {
		return channel.Name == name
	})
}

// API channels.join: Joins a channel, creating it if needed.
func (sl *Slack) JoinChannel(name string) error {
	uv := sl.urlValues()
	uv.Add("name", name)

	_, err := sl.GetRequest(channelsJoinApiEndpoint, uv)
	if err != nil {
		return err
	}
	return nil
}

type Message struct {
	Type    string `json:"type"`
	Ts      string `json:"ts"`
	UserId  string `json:"user"`
	Text    string `json:"text"`
	Subtype string `json:"subtype"`
}

func (msg *Message) Timestamp() *time.Time {
	tsf, _ := strconv.ParseFloat(msg.Ts, 64)
	ts := time.Unix(int64(tsf), 0)
	return &ts
}

// option type for `channels.history` api
type ChannelsHistoryOpt struct {
	Channel   string  `json:"channel"`
	Latest    float64 `json:"latest"`
	Oldest    float64 `json:"oldest"`
	Inclusive int     `json:"inclusive"`
	Count     int     `json:"count"`
	UnReads   int     `json:"unreads,omitempty"`
}

func (opt *ChannelsHistoryOpt) Bind(uv *url.Values) error {
	uv.Add("channel", opt.Channel)
	if opt.Latest != 0.0 {
		uv.Add("lastest", strconv.FormatFloat(opt.Latest, 'f', 6, 64))
	}
	if opt.Oldest != 0.0 {
		uv.Add("oldest", strconv.FormatFloat(opt.Oldest, 'f', 6, 64))
	}
	uv.Add("inclusive", strconv.Itoa(opt.Inclusive))
	if opt.Count != 0 {
		uv.Add("count", strconv.Itoa(opt.Count))
	}
	uv.Add("unreads", strconv.Itoa(opt.UnReads))
	return nil
}

// response type for `channels.history` api
type ChannelsHistoryResponse struct {
	BaseAPIResponse
	Latest             float64    `json:"latest"`
	Messages           []*Message `json:"messages"`
	HasMore            bool       `json:"has_more"`
	UnReadCountDisplay int        `json:"unread_count_display"`
}

// API channels.history: Fetches history of messages and events from a channel.
func (sl *Slack) ChannelsHistory(opt *ChannelsHistoryOpt) (*ChannelsHistoryResponse, error) {
	uv := sl.urlValues()
	err := opt.Bind(uv)
	if err != nil {
		return nil, err
	}
	body, err := sl.GetRequest(channelsHistoryApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(ChannelsHistoryResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res, nil
}

func (sl *Slack) ChannelsHistoryMessages(opt *ChannelsHistoryOpt) ([]*Message, error) {
	res, err := sl.ChannelsHistory(opt)
	if err != nil {
		return nil, err
	}
	return res.Messages, nil
}
<|endoftext|>


<commit_before>package syslutil

import sysl "github.com/anz-bank/sysl/src/proto"

// GetTypeDetail returns name of the type and details in string format
func GetTypeDetail(t *sysl.Type) (typeName string, typeDetail string) {
	switch x := t.Type.(type) {
	case *sysl.Type_Primitive_:
		typeName = "primitive"
		typeDetail = sysl.Type_Primitive_name[int32(x.Primitive)]
	case *sysl.Type_TypeRef:
		typeName = "type_ref"
		if x.TypeRef.Ref != nil && len(x.TypeRef.Ref.Path) == 1 {
			typeDetail = x.TypeRef.Ref.Path[0]
			if x.TypeRef.Ref.Appname != nil {
				typeDetail = x.TypeRef.Ref.Appname.Part[0] + "." + typeDetail
			}
		} else {
			typeDetail = x.TypeRef.Ref.Appname.Part[0]
		}

	case *sysl.Type_Sequence:
		typeName = "sequence"
		_, d := GetTypeDetail(x.Sequence)
		typeDetail = d
	case *sysl.Type_Set:
		typeName = "set"
		_, d := GetTypeDetail(x.Set)
		typeDetail = d
	case *sysl.Type_List_:
		typeName = "list"
		_, d := GetTypeDetail(x.List.Type)
		typeDetail = d
	case *sysl.Type_Tuple_:
		typeName = "tuple"
	case *sysl.Type_Relation_:
		typeName = "relation"
	case *sysl.Type_OneOf_:
		typeName = "union"
	}
	return typeName, typeDetail
}

// TypeNone returns none-type
func TypeNone() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_NoType_{NoType: &sysl.Type_NoType{}}}
}

// TypeEmpty returns empty primitive type
func TypeEmpty() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_EMPTY}}
}

// TypeString returns string type
func TypeString() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_STRING}}
}

// TypeInt returns integer type
func TypeInt() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_INT}}
}

// TypeFloat returns float type
func TypeFloat() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_FLOAT}}
}

// TypeDecimal returns decimal type
func TypeDecimal() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_DECIMAL}}
}

// TypeBool returns boolean type
func TypeBool() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_BOOL}}
}

// HasSameType returns true if type 2 matches with type 1
func HasSameType(type1 *sysl.Type, type2 *sysl.Type) bool {
	if type1 == nil || type2 == nil {
		return false
	}

	switch type1.GetType().(type) {
	case *sysl.Type_Primitive_:
		return type1.GetPrimitive() == type2.GetPrimitive()
	case *sysl.Type_TypeRef:
		if type2.GetTypeRef() != nil {
			ref1 := type1.GetTypeRef().GetRef()
			ref2 := type2.GetTypeRef().GetRef()

			if ref1.GetAppname() != nil && ref2.GetAppname() != nil {
				return ref1.GetAppname().GetPart()[0] == ref2.GetAppname().GetPart()[0]
			} else if ref1.GetPath() != nil && ref2.GetPath() != nil {
				return ref1.GetPath()[0] == ref2.GetPath()[0]
			}
		}
	case *sysl.Type_Tuple_:
		return type2.GetTuple() != nil
	}

	return false
}
<commit_msg>Fix GetTypeDetail()<commit_after>package syslutil

import (
	"strings"

	sysl "github.com/anz-bank/sysl/src/proto"
)

// GetTypeDetail returns name of the type and details in string format
func GetTypeDetail(t *sysl.Type) (typeName string, typeDetail string) {
	switch x := t.Type.(type) {
	case *sysl.Type_Primitive_:
		typeName = "primitive"
		typeDetail = sysl.Type_Primitive_name[int32(x.Primitive)]
	case *sysl.Type_TypeRef:
		typeName = "type_ref"
		switch {
		case x.TypeRef.Ref != nil && len(x.TypeRef.Ref.Path) == 1:
			typeDetail = x.TypeRef.Ref.Path[0]
		case x.TypeRef.Ref.Appname != nil:
			typeDetail = x.TypeRef.Ref.Appname.Part[0]
		default:
			typeDetail = strings.Join(x.TypeRef.Ref.Path, ".")
		}
	case *sysl.Type_Sequence:
		typeName = "sequence"
		_, d := GetTypeDetail(x.Sequence)
		typeDetail = d
	case *sysl.Type_Set:
		typeName = "set"
		_, d := GetTypeDetail(x.Set)
		typeDetail = d
	case *sysl.Type_List_:
		typeName = "list"
		_, d := GetTypeDetail(x.List.Type)
		typeDetail = d
	case *sysl.Type_Tuple_:
		typeName = "tuple"
	case *sysl.Type_Relation_:
		typeName = "relation"
	case *sysl.Type_OneOf_:
		typeName = "union"
	}
	return typeName, typeDetail
}

// TypeNone returns none-type
func TypeNone() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_NoType_{NoType: &sysl.Type_NoType{}}}
}

// TypeEmpty returns empty primitive type
func TypeEmpty() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_EMPTY}}
}

// TypeString returns string type
func TypeString() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_STRING}}
}

// TypeInt returns integer type
func TypeInt() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_INT}}
}

// TypeFloat returns float type
func TypeFloat() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_FLOAT}}
}

// TypeDecimal returns decimal type
func TypeDecimal() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_DECIMAL}}
}

// TypeBool returns boolean type
func TypeBool() *sysl.Type {
	return &sysl.Type{Type: &sysl.Type_Primitive_{Primitive: sysl.Type_BOOL}}
}

// HasSameType returns true if type 2 matches with type 1
func HasSameType(type1 *sysl.Type, type2 *sysl.Type) bool {
	if type1 == nil || type2 == nil {
		return false
	}

	switch type1.GetType().(type) {
	case *sysl.Type_Primitive_:
		return type1.GetPrimitive() == type2.GetPrimitive()
	case *sysl.Type_TypeRef:
		if type2.GetTypeRef() != nil {
			ref1 := type1.GetTypeRef().GetRef()
			ref2 := type2.GetTypeRef().GetRef()

			if ref1.GetAppname() != nil && ref2.GetAppname() != nil {
				return ref1.GetAppname().GetPart()[0] == ref2.GetAppname().GetPart()[0]
			} else if ref1.GetPath() != nil && ref2.GetPath() != nil {
				return ref1.GetPath()[0] == ref2.GetPath()[0]
			}
		}
	case *sysl.Type_Tuple_:
		return type2.GetTuple() != nil
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"

	pb "github.com/creiht/formic/proto"
	"github.com/pandemicsyn/ftls"
	"github.com/pandemicsyn/oort/api"
	"github.com/pandemicsyn/oort/oort"

	"net"
)

var (
	usetls              = flag.Bool("tls", true, "Connection uses TLS if true, else plain TCP")
	certFile            = flag.String("cert_file", "/var/lib/formic/server.crt", "The TLS cert file")
	keyFile             = flag.String("key_file", "/var/lib/formic/server.key", "The TLS key file")
	port                = flag.Int("port", 8445, "The server port")
	oortValueSyndicate  = flag.String("oortvaluesyndicate", "", "Syndicate server for value store information")
	oortGroupSyndicate  = flag.String("oortgroupsyndicate", "", "Syndicate server for group store information")
	oortValueRing       = flag.String("oortvaluering", "/var/lib/formic/ring/valuestore.ring", "Location of cached value store ring file")
	oortGroupRing       = flag.String("oortgroupring", "/var/lib/formic/ring/groupstore.ring", "Location of cached value store ring file")
	insecureSkipVerify  = flag.Bool("skipverify", false, "don't verify cert")
	oortClientMutualTLS = flag.Bool("mutualtls", true, "whether or not the server expects mutual tls auth")
	oortClientCert      = flag.String("oort-client-cert", "/var/lib/formic/client.crt", "cert file to use")
	oortClientKey       = flag.String("oort-client-key", "/var/lib/formic/client.key", "key file to use")
	oortClientCA        = flag.String("oort-client-ca", "/var/lib/formic/ca.pem", "ca file to use")
)

// FatalIf is just a lazy log/panic on error func
func FatalIf(err error, msg string) {
	if err != nil {
		grpclog.Fatalf("%s: %v", msg, err)
	}
}

func main() {
	flag.Parse()

	envtls := os.Getenv("FORMICD_TLS")
	if envtls == "true" {
		*usetls = true
	}

	envoortvsyndicate := os.Getenv("FORMICD_OORT_VALUE_SYNDICATE")
	if envoortvsyndicate != "" {
		*oortValueSyndicate = envoortvsyndicate
	}
	if *oortValueSyndicate == "" {
		var err error
		*oortValueSyndicate, err = oort.GenServiceID("value", "syndicate", "tcp")
		if err != nil {
			FatalIf(err, "couldn't resolve value store syndicate")
		}
	}

	envoortgsyndicate := os.Getenv("FORMICD_OORT_GROUP_SYNDICATE")
	if envoortgsyndicate != "" {
		*oortGroupSyndicate = envoortgsyndicate
	}
	if *oortGroupSyndicate == "" {
		var err error
		*oortGroupSyndicate, err = oort.GenServiceID("group", "syndicate", "tcp")
		if err != nil {
			FatalIf(err, "couldn't resolve group store syndicate")
		}
	}

	envoortvring := os.Getenv("FORMICD_OORT_VALUE_RING")
	if envoortvring != "" {
		*oortValueRing = envoortvring
	}

	envoortgring := os.Getenv("FORMICD_OORT_GROUP_RING")
	if envoortgring != "" {
		*oortGroupRing = envoortgring
	}

	envport := os.Getenv("FORMICD_PORT")
	if envport != "" {
		p, err := strconv.Atoi(envport)
		if err != nil {
			log.Println("Did not send valid port from env:", err)
		} else {
			*port = p
		}
	}

	envcert := os.Getenv("FORMICD_CERT_FILE")
	if envcert != "" {
		*certFile = envcert
	}

	envkey := os.Getenv("FORMICD_KEY_FILE")
	if envkey != "" {
		*keyFile = envkey
	}
	envSkipVerify := os.Getenv("FORMICD_INSECURE_SKIP_VERIFY")
	if envSkipVerify == "true" {
		*insecureSkipVerify = true
	}
	envMutualTLS := os.Getenv("FORMICD_MUTUAL_TLS")
	if envMutualTLS == "true" {
		*oortClientMutualTLS = true
	}
	envClientCA := os.Getenv("FORMICD_CLIENT_CA_FILE")
	if envClientCA != "" {
		*oortClientCA = envClientCA
	}
	envClientCert := os.Getenv("FORMICD_CLIENT_CERT_FILE")
	if envClientCert != "" {
		*oortClientCert = envClientCert
	}
	envClientKey := os.Getenv("FORMICD_CLIENT_KEY_FILE")
	if envClientKey != "" {
		*oortClientKey = envClientKey
	}

	var opts []grpc.ServerOption
	if *usetls {
		creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
		FatalIf(err, "Couldn't load cert from file")
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}
	s := grpc.NewServer(opts...)
	copt, err := ftls.NewGRPCClientDialOpt(&ftls.Config{
		MutualTLS:          *oortClientMutualTLS,
		InsecureSkipVerify: *insecureSkipVerify,
		CertFile:           *oortClientCert,
		KeyFile:            *oortClientKey,
		CAFile:             *oortClientCA,
	})
	if err != nil {
		grpclog.Fatalln("Cannot setup tls config:", err)
	}

	vstore := api.NewReplValueStore(&api.ReplValueStoreConfig{
		AddressIndex:       2,
		GRPCOpts:           []grpc.DialOption{copt},
		RingServer:         *oortValueSyndicate,
		RingCachePath:      *oortValueRing,
		RingServerGRPCOpts: []grpc.DialOption{copt},
	})
	if err := vstore.Startup(context.Background()); err != nil {
		grpclog.Fatalln("Cannot start valuestore connector:", err)
	}

	gstore := api.NewReplGroupStore(&api.ReplGroupStoreConfig{
		AddressIndex:       2,
		GRPCOpts:           []grpc.DialOption{copt},
		RingServer:         *oortGroupSyndicate,
		RingCachePath:      *oortGroupRing,
		RingServerGRPCOpts: []grpc.DialOption{copt},
	})
	if err := gstore.Startup(context.Background()); err != nil {
		grpclog.Fatalln("Cannot start valuestore connector:", err)
	}

	fs, err := NewOortFS(vstore, gstore)
	if err != nil {
		grpclog.Fatalln(err)
	}
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	FatalIf(err, "Failed to bind to port")
	pb.RegisterApiServer(s, NewApiServer(fs))
	grpclog.Printf("Starting up on %d...\n", *port)
	s.Serve(l)
}
<commit_msg>Touchups with Oort API interactions<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"

	pb "github.com/creiht/formic/proto"
	"github.com/pandemicsyn/ftls"
	"github.com/pandemicsyn/oort/api"

	"net"
)

var (
	usetls              = flag.Bool("tls", true, "Connection uses TLS if true, else plain TCP")
	certFile            = flag.String("cert_file", "/var/lib/formic/server.crt", "The TLS cert file")
	keyFile             = flag.String("key_file", "/var/lib/formic/server.key", "The TLS key file")
	port                = flag.Int("port", 8445, "The server port")
	oortValueSyndicate  = flag.String("oortvaluesyndicate", "", "Syndicate server for value store information; empty string will use default DNS SRV record")
	oortGroupSyndicate  = flag.String("oortgroupsyndicate", "", "Syndicate server for group store information; empty string will use default DNS SRV record")
	oortValueRing       = flag.String("oortvaluering", "/var/lib/formic/ring/valuestore.ring", "Location of cached value store ring file")
	oortGroupRing       = flag.String("oortgroupring", "/var/lib/formic/ring/groupstore.ring", "Location of cached value store ring file")
	insecureSkipVerify  = flag.Bool("skipverify", false, "don't verify cert")
	oortClientMutualTLS = flag.Bool("mutualtls", true, "whether or not the server expects mutual tls auth")
	oortClientCert      = flag.String("oort-client-cert", "/var/lib/formic/client.crt", "cert file to use")
	oortClientKey       = flag.String("oort-client-key", "/var/lib/formic/client.key", "key file to use")
	oortClientCA        = flag.String("oort-client-ca", "/var/lib/formic/ca.pem", "ca file to use")
)

// FatalIf is just a lazy log/panic on error func
func FatalIf(err error, msg string) {
	if err != nil {
		grpclog.Fatalf("%s: %v", msg, err)
	}
}

func main() {
	flag.Parse()

	envtls := os.Getenv("FORMICD_TLS")
	if envtls == "true" {
		*usetls = true
	}

	envoortvsyndicate := os.Getenv("FORMICD_OORT_VALUE_SYNDICATE")
	if envoortvsyndicate != "" {
		*oortValueSyndicate = envoortvsyndicate
	}

	envoortgsyndicate := os.Getenv("FORMICD_OORT_GROUP_SYNDICATE")
	if envoortgsyndicate != "" {
		*oortGroupSyndicate = envoortgsyndicate
	}

	envoortvring := os.Getenv("FORMICD_OORT_VALUE_RING")
	if envoortvring != "" {
		*oortValueRing = envoortvring
	}

	envoortgring := os.Getenv("FORMICD_OORT_GROUP_RING")
	if envoortgring != "" {
		*oortGroupRing = envoortgring
	}

	envport := os.Getenv("FORMICD_PORT")
	if envport != "" {
		p, err := strconv.Atoi(envport)
		if err != nil {
			log.Println("Did not send valid port from env:", err)
		} else {
			*port = p
		}
	}

	envcert := os.Getenv("FORMICD_CERT_FILE")
	if envcert != "" {
		*certFile = envcert
	}

	envkey := os.Getenv("FORMICD_KEY_FILE")
	if envkey != "" {
		*keyFile = envkey
	}
	envSkipVerify := os.Getenv("FORMICD_INSECURE_SKIP_VERIFY")
	if envSkipVerify == "true" {
		*insecureSkipVerify = true
	}
	envMutualTLS := os.Getenv("FORMICD_MUTUAL_TLS")
	if envMutualTLS == "true" {
		*oortClientMutualTLS = true
	}
	envClientCA := os.Getenv("FORMICD_CLIENT_CA_FILE")
	if envClientCA != "" {
		*oortClientCA = envClientCA
	}
	envClientCert := os.Getenv("FORMICD_CLIENT_CERT_FILE")
	if envClientCert != "" {
		*oortClientCert = envClientCert
	}
	envClientKey := os.Getenv("FORMICD_CLIENT_KEY_FILE")
	if envClientKey != "" {
		*oortClientKey = envClientKey
	}

	var opts []grpc.ServerOption
	if *usetls {
		creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
		FatalIf(err, "Couldn't load cert from file")
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}
	s := grpc.NewServer(opts...)
	copt, err := ftls.NewGRPCClientDialOpt(&ftls.Config{
		MutualTLS:          *oortClientMutualTLS,
		InsecureSkipVerify: *insecureSkipVerify,
		CertFile:           *oortClientCert,
		KeyFile:            *oortClientKey,
		CAFile:             *oortClientCA,
	})
	if err != nil {
		grpclog.Fatalln("Cannot setup tls config:", err)
	}

	clientID, _ := os.Hostname()
	if clientID != "" {
		clientID += "/formicd"
	}

	vstore := api.NewReplValueStore(&api.ReplValueStoreConfig{
		AddressIndex:       2,
		GRPCOpts:           []grpc.DialOption{copt},
		RingServer:         *oortValueSyndicate,
		RingCachePath:      *oortValueRing,
		RingServerGRPCOpts: []grpc.DialOption{copt},
		RingClientID:       clientID,
	})
	if err := vstore.Startup(context.Background()); err != nil {
		grpclog.Fatalln("Cannot start valuestore connector:", err)
	}

	gstore := api.NewReplGroupStore(&api.ReplGroupStoreConfig{
		AddressIndex:       2,
		GRPCOpts:           []grpc.DialOption{copt},
		RingServer:         *oortGroupSyndicate,
		RingCachePath:      *oortGroupRing,
		RingServerGRPCOpts: []grpc.DialOption{copt},
		RingClientID:       clientID,
	})
	if err := gstore.Startup(context.Background()); err != nil {
		grpclog.Fatalln("Cannot start valuestore connector:", err)
	}

	fs, err := NewOortFS(vstore, gstore)
	if err != nil {
		grpclog.Fatalln(err)
	}
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	FatalIf(err, "Failed to bind to port")
	pb.RegisterApiServer(s, NewApiServer(fs))
	grpclog.Printf("Starting up on %d...\n", *port)
	s.Serve(l)
}
<|endoftext|>