<commit_before>// Copyright 2017 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"log"
	"os"

	"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
	"github.com/tsuru/tsuru-client/tsuru/admin"
	"github.com/tsuru/tsuru-client/tsuru/client"
	"github.com/tsuru/tsuru-client/tsuru/installer"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/iaas/dockermachine"
	_ "github.com/tsuru/tsuru/provision/docker/cmds"
)

const (
	version = "1.6.0-rc3"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(context *cmd.Context) error {
		return client.RunPlugin(context)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&client.AppRun{})
	m.Register(&client.AppInfo{})
	m.Register(&client.AppCreate{})
	m.Register(&client.AppRemove{})
	m.Register(&client.AppUpdate{})
	m.Register(&client.UnitAdd{})
	m.Register(&client.UnitRemove{})
	m.Register(&client.AppList{})
	m.Register(&client.AppLog{})
	m.Register(&client.AppGrant{})
	m.Register(&client.AppRevoke{})
	m.Register(&client.AppRestart{})
	m.Register(&client.AppStart{})
	m.Register(&client.AppStop{})
	m.Register(&client.Init{})
	m.Register(&admin.AppLockDelete{})
	m.Register(&client.CertificateSet{})
	m.Register(&client.CertificateUnset{})
	m.Register(&client.CertificateList{})
	m.Register(&client.CnameAdd{})
	m.Register(&client.CnameRemove{})
	m.Register(&client.EnvGet{})
	m.Register(&client.EnvSet{})
	m.Register(&client.EnvUnset{})
	m.Register(&client.KeyAdd{})
	m.Register(&client.KeyRemove{})
	m.Register(&client.KeyList{})
	m.Register(client.ServiceList{})
	m.Register(&client.ServiceInstanceAdd{})
	m.Register(&client.ServiceInstanceUpdate{})
	m.Register(&client.ServiceInstanceRemove{})
	m.Register(client.ServiceInfo{})
	m.Register(client.ServiceInstanceInfo{})
	m.Register(client.ServiceInstanceStatus{})
	m.Register(&client.ServiceInstanceGrant{})
	m.Register(&client.ServiceInstanceRevoke{})
	m.Register(&client.ServiceInstanceBind{})
	m.Register(&client.ServiceInstanceUnbind{})
	m.Register(&admin.PlatformList{})
	m.Register(&admin.PlatformAdd{})
	m.Register(&admin.PlatformUpdate{})
	m.Register(&admin.PlatformRemove{})
	m.Register(&admin.PlatformInfo{})
	m.Register(&client.PluginInstall{})
	m.Register(&client.PluginRemove{})
	m.Register(&client.PluginList{})
	m.Register(&client.AppSwap{})
	m.Register(&client.AppDeploy{})
	m.Register(&client.AppBuild{})
	m.Register(&client.PlanList{})
	m.Register(&client.UserCreate{})
	m.Register(&client.ResetPassword{})
	m.Register(&client.UserRemove{})
	m.Register(&client.ListUsers{})
	m.Register(&client.TeamCreate{})
	m.Register(&client.TeamUpdate{})
	m.Register(&client.TeamRemove{})
	m.Register(&client.TeamList{})
	m.Register(&client.TeamInfo{})
	m.Register(&client.ChangePassword{})
	m.Register(&client.ShowAPIToken{})
	m.Register(&client.RegenerateAPIToken{})
	m.Register(&client.AppDeployList{})
	m.Register(&client.AppDeployRollback{})
	m.Register(&client.AppDeployRollbackUpdate{})
	m.Register(&client.AppDeployRebuild{})
	m.Register(&cmd.ShellToContainerCmd{})
	m.Register(&client.PoolList{})
	m.Register(&client.PermissionList{})
	m.Register(&client.RoleAdd{})
	m.Register(&client.RoleUpdate{})
	m.Register(&client.RoleRemove{})
	m.Register(&client.RoleList{})
	m.Register(&client.RoleInfo{})
	m.Register(&client.RolePermissionAdd{})
	m.Register(&client.RolePermissionRemove{})
	m.Register(&client.RoleAssign{})
	m.Register(&client.RoleDissociate{})
	m.Register(&client.RoleDefaultAdd{})
	m.Register(&client.RoleDefaultList{})
	m.Register(&client.RoleDefaultRemove{})
	m.Register(&installer.Install{})
	m.Register(&installer.Uninstall{})
	m.Register(&installer.InstallHostList{})
	m.Register(&installer.InstallSSH{})
	m.Register(&installer.InstallConfigInit{})
	m.Register(&admin.AddPoolToSchedulerCmd{})
	m.Register(&client.EventList{})
	m.Register(&client.EventInfo{})
	m.Register(&client.EventCancel{})
	m.Register(&client.RoutersList{})
	m.Register(&admin.TemplateList{})
	m.Register(&admin.TemplateAdd{})
	m.Register(&admin.TemplateRemove{})
	m.Register(&admin.MachineList{})
	m.Register(&admin.MachineDestroy{})
	m.Register(&admin.TemplateUpdate{})
	m.Register(&admin.PlanCreate{})
	m.Register(&admin.PlanRemove{})
	m.Register(&admin.UpdatePoolToSchedulerCmd{})
	m.Register(&admin.RemovePoolFromSchedulerCmd{})
	m.Register(&admin.ServiceCreate{})
	m.Register(&admin.ServiceDestroy{})
	m.Register(&admin.ServiceUpdate{})
	m.Register(&admin.ServiceDocGet{})
	m.Register(&admin.ServiceDocAdd{})
	m.Register(&admin.ServiceTemplate{})
	m.Register(&admin.UserQuotaView{})
	m.Register(&admin.UserChangeQuota{})
	m.Register(&admin.AppQuotaView{})
	m.Register(&admin.AppQuotaChange{})
	m.Register(&admin.AppRoutesRebuild{})
	m.Register(&admin.PoolConstraintList{})
	m.Register(&admin.PoolConstraintSet{})
	m.Register(&admin.EventBlockList{})
	m.Register(&admin.EventBlockAdd{})
	m.Register(&admin.EventBlockRemove{})
	m.Register(&client.TagList{})
	m.Register(&admin.NodeContainerList{})
	m.Register(&admin.NodeContainerAdd{})
	m.Register(&admin.NodeContainerInfo{})
	m.Register(&admin.NodeContainerUpdate{})
	m.Register(&admin.NodeContainerDelete{})
	m.Register(&admin.NodeContainerUpgrade{})
	m.Register(&admin.ClusterAdd{})
	m.Register(&admin.ClusterUpdate{})
	m.Register(&admin.ClusterRemove{})
	m.Register(&admin.ClusterList{})
	m.Register(&client.VolumeCreate{})
	m.Register(&client.VolumeUpdate{})
	m.Register(&client.VolumeList{})
	m.Register(&client.VolumePlansList{})
	m.Register(&client.VolumeDelete{})
	m.Register(&client.VolumeBind{})
	m.Register(&client.VolumeUnbind{})
	m.Register(&client.AppRoutersList{})
	m.Register(&client.AppRoutersAdd{})
	m.Register(&client.AppRoutersRemove{})
	m.Register(&client.AppRoutersUpdate{})
	m.Register(&admin.InfoNodeCmd{})
	m.Register(&client.TokenCreateCmd{})
	m.Register(&client.TokenUpdateCmd{})
	m.Register(&client.TokenListCmd{})
	m.Register(&client.TokenDeleteCmd{})
	m.Register(&client.WebhookList{})
	m.Register(&client.WebhookCreate{})
	m.Register(&client.WebhookUpdate{})
	m.Register(&client.WebhookDelete{})
	m.Register(&admin.BrokerList{})
	m.Register(&admin.BrokerAdd{})
	m.Register(&admin.BrokerUpdate{})
	m.Register(&admin.BrokerDelete{})
	m.RegisterRemoved("bs-env-set", "You should use `tsuru node-container-update big-sibling` instead.")
	m.RegisterRemoved("bs-info", "You should use `tsuru node-container-info big-sibling` instead.")
	m.RegisterRemoved("bs-upgrade", "You should use `tsuru node-container-upgrade big-sibling` instead.")
	m.RegisterDeprecated(&admin.AddTeamsToPoolCmd{}, "pool-teams-add")
	m.RegisterDeprecated(&admin.RemoveTeamsFromPoolCmd{}, "pool-teams-remove")
	m.RegisterDeprecated(&admin.AddNodeCmd{}, "docker-node-add")
	m.RegisterDeprecated(&admin.RemoveNodeCmd{}, "docker-node-remove")
	m.RegisterDeprecated(&admin.UpdateNodeCmd{}, "docker-node-update")
	m.RegisterDeprecated(&admin.ListNodesCmd{}, "docker-node-list")
	m.RegisterDeprecated(&admin.GetNodeHealingConfigCmd{}, "docker-healing-info")
	m.RegisterDeprecated(&admin.SetNodeHealingConfigCmd{}, "docker-healing-update")
	m.RegisterDeprecated(&admin.DeleteNodeHealingConfigCmd{}, "docker-healing-delete")
	m.RegisterDeprecated(&admin.RebalanceNodeCmd{}, "containers-rebalance")
	m.RegisterDeprecated(&admin.AutoScaleRunCmd{}, "docker-autoscale-run")
	m.RegisterDeprecated(&admin.ListAutoScaleHistoryCmd{}, "docker-autoscale-list")
	m.RegisterDeprecated(&admin.AutoScaleInfoCmd{}, "docker-autoscale-info")
	m.RegisterDeprecated(&admin.AutoScaleSetRuleCmd{}, "docker-autoscale-rule-set")
	m.RegisterDeprecated(&admin.AutoScaleDeleteRuleCmd{}, "docker-autoscale-rule-remove")
	m.RegisterDeprecated(&admin.ListHealingHistoryCmd{}, "docker-healing-list")
	registerExtraCommands(m)
	return m
}

func registerExtraCommands(m *cmd.Manager) {
	for _, c := range cmd.ExtraCmds() {
		m.Register(c)
	}
}

func inDockerMachineDriverMode() bool {
	return os.Getenv(localbinary.PluginEnvKey) == localbinary.PluginEnvVal
}

func main() {
	if inDockerMachineDriverMode() {
		err := dockermachine.RunDriver(os.Getenv(localbinary.PluginEnvDriverName))
		if err != nil {
			log.Fatalf("Error running driver: %s", err)
		}
	} else {
		localbinary.CurrentBinaryIsDockerMachine = true
		name := cmd.ExtractProgramName(os.Args[0])
		m := buildManager(name)
		m.Run(os.Args[1:])
	}
}
<commit_msg>Bump version to 1.6.0<commit_after>// Copyright 2017 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"log"
	"os"

	"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
	"github.com/tsuru/tsuru-client/tsuru/admin"
	"github.com/tsuru/tsuru-client/tsuru/client"
	"github.com/tsuru/tsuru-client/tsuru/installer"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/iaas/dockermachine"
	_ "github.com/tsuru/tsuru/provision/docker/cmds"
)

const (
	version = "1.6.0"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(context *cmd.Context) error {
		return client.RunPlugin(context)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&client.AppRun{})
	m.Register(&client.AppInfo{})
	m.Register(&client.AppCreate{})
	m.Register(&client.AppRemove{})
	m.Register(&client.AppUpdate{})
	m.Register(&client.UnitAdd{})
	m.Register(&client.UnitRemove{})
	m.Register(&client.AppList{})
	m.Register(&client.AppLog{})
	m.Register(&client.AppGrant{})
	m.Register(&client.AppRevoke{})
	m.Register(&client.AppRestart{})
	m.Register(&client.AppStart{})
	m.Register(&client.AppStop{})
	m.Register(&client.Init{})
	m.Register(&admin.AppLockDelete{})
	m.Register(&client.CertificateSet{})
	m.Register(&client.CertificateUnset{})
	m.Register(&client.CertificateList{})
	m.Register(&client.CnameAdd{})
	m.Register(&client.CnameRemove{})
	m.Register(&client.EnvGet{})
	m.Register(&client.EnvSet{})
	m.Register(&client.EnvUnset{})
	m.Register(&client.KeyAdd{})
	m.Register(&client.KeyRemove{})
	m.Register(&client.KeyList{})
	m.Register(client.ServiceList{})
	m.Register(&client.ServiceInstanceAdd{})
	m.Register(&client.ServiceInstanceUpdate{})
	m.Register(&client.ServiceInstanceRemove{})
	m.Register(client.ServiceInfo{})
	m.Register(client.ServiceInstanceInfo{})
	m.Register(client.ServiceInstanceStatus{})
	m.Register(&client.ServiceInstanceGrant{})
	m.Register(&client.ServiceInstanceRevoke{})
	m.Register(&client.ServiceInstanceBind{})
	m.Register(&client.ServiceInstanceUnbind{})
	m.Register(&admin.PlatformList{})
	m.Register(&admin.PlatformAdd{})
	m.Register(&admin.PlatformUpdate{})
	m.Register(&admin.PlatformRemove{})
	m.Register(&admin.PlatformInfo{})
	m.Register(&client.PluginInstall{})
	m.Register(&client.PluginRemove{})
	m.Register(&client.PluginList{})
	m.Register(&client.AppSwap{})
	m.Register(&client.AppDeploy{})
	m.Register(&client.AppBuild{})
	m.Register(&client.PlanList{})
	m.Register(&client.UserCreate{})
	m.Register(&client.ResetPassword{})
	m.Register(&client.UserRemove{})
	m.Register(&client.ListUsers{})
	m.Register(&client.TeamCreate{})
	m.Register(&client.TeamUpdate{})
	m.Register(&client.TeamRemove{})
	m.Register(&client.TeamList{})
	m.Register(&client.TeamInfo{})
	m.Register(&client.ChangePassword{})
	m.Register(&client.ShowAPIToken{})
	m.Register(&client.RegenerateAPIToken{})
	m.Register(&client.AppDeployList{})
	m.Register(&client.AppDeployRollback{})
	m.Register(&client.AppDeployRollbackUpdate{})
	m.Register(&client.AppDeployRebuild{})
	m.Register(&cmd.ShellToContainerCmd{})
	m.Register(&client.PoolList{})
	m.Register(&client.PermissionList{})
	m.Register(&client.RoleAdd{})
	m.Register(&client.RoleUpdate{})
	m.Register(&client.RoleRemove{})
	m.Register(&client.RoleList{})
	m.Register(&client.RoleInfo{})
	m.Register(&client.RolePermissionAdd{})
	m.Register(&client.RolePermissionRemove{})
	m.Register(&client.RoleAssign{})
	m.Register(&client.RoleDissociate{})
	m.Register(&client.RoleDefaultAdd{})
	m.Register(&client.RoleDefaultList{})
	m.Register(&client.RoleDefaultRemove{})
	m.Register(&installer.Install{})
	m.Register(&installer.Uninstall{})
	m.Register(&installer.InstallHostList{})
	m.Register(&installer.InstallSSH{})
	m.Register(&installer.InstallConfigInit{})
	m.Register(&admin.AddPoolToSchedulerCmd{})
	m.Register(&client.EventList{})
	m.Register(&client.EventInfo{})
	m.Register(&client.EventCancel{})
	m.Register(&client.RoutersList{})
	m.Register(&admin.TemplateList{})
	m.Register(&admin.TemplateAdd{})
	m.Register(&admin.TemplateRemove{})
	m.Register(&admin.MachineList{})
	m.Register(&admin.MachineDestroy{})
	m.Register(&admin.TemplateUpdate{})
	m.Register(&admin.PlanCreate{})
	m.Register(&admin.PlanRemove{})
	m.Register(&admin.UpdatePoolToSchedulerCmd{})
	m.Register(&admin.RemovePoolFromSchedulerCmd{})
	m.Register(&admin.ServiceCreate{})
	m.Register(&admin.ServiceDestroy{})
	m.Register(&admin.ServiceUpdate{})
	m.Register(&admin.ServiceDocGet{})
	m.Register(&admin.ServiceDocAdd{})
	m.Register(&admin.ServiceTemplate{})
	m.Register(&admin.UserQuotaView{})
	m.Register(&admin.UserChangeQuota{})
	m.Register(&admin.AppQuotaView{})
	m.Register(&admin.AppQuotaChange{})
	m.Register(&admin.AppRoutesRebuild{})
	m.Register(&admin.PoolConstraintList{})
	m.Register(&admin.PoolConstraintSet{})
	m.Register(&admin.EventBlockList{})
	m.Register(&admin.EventBlockAdd{})
	m.Register(&admin.EventBlockRemove{})
	m.Register(&client.TagList{})
	m.Register(&admin.NodeContainerList{})
	m.Register(&admin.NodeContainerAdd{})
	m.Register(&admin.NodeContainerInfo{})
	m.Register(&admin.NodeContainerUpdate{})
	m.Register(&admin.NodeContainerDelete{})
	m.Register(&admin.NodeContainerUpgrade{})
	m.Register(&admin.ClusterAdd{})
	m.Register(&admin.ClusterUpdate{})
	m.Register(&admin.ClusterRemove{})
	m.Register(&admin.ClusterList{})
	m.Register(&client.VolumeCreate{})
	m.Register(&client.VolumeUpdate{})
	m.Register(&client.VolumeList{})
	m.Register(&client.VolumePlansList{})
	m.Register(&client.VolumeDelete{})
	m.Register(&client.VolumeBind{})
	m.Register(&client.VolumeUnbind{})
	m.Register(&client.AppRoutersList{})
	m.Register(&client.AppRoutersAdd{})
	m.Register(&client.AppRoutersRemove{})
	m.Register(&client.AppRoutersUpdate{})
	m.Register(&admin.InfoNodeCmd{})
	m.Register(&client.TokenCreateCmd{})
	m.Register(&client.TokenUpdateCmd{})
	m.Register(&client.TokenListCmd{})
	m.Register(&client.TokenDeleteCmd{})
	m.Register(&client.WebhookList{})
	m.Register(&client.WebhookCreate{})
	m.Register(&client.WebhookUpdate{})
	m.Register(&client.WebhookDelete{})
	m.Register(&admin.BrokerList{})
	m.Register(&admin.BrokerAdd{})
	m.Register(&admin.BrokerUpdate{})
	m.Register(&admin.BrokerDelete{})
	m.RegisterRemoved("bs-env-set", "You should use `tsuru node-container-update big-sibling` instead.")
	m.RegisterRemoved("bs-info", "You should use `tsuru node-container-info big-sibling` instead.")
	m.RegisterRemoved("bs-upgrade", "You should use `tsuru node-container-upgrade big-sibling` instead.")
	m.RegisterDeprecated(&admin.AddTeamsToPoolCmd{}, "pool-teams-add")
	m.RegisterDeprecated(&admin.RemoveTeamsFromPoolCmd{}, "pool-teams-remove")
	m.RegisterDeprecated(&admin.AddNodeCmd{}, "docker-node-add")
	m.RegisterDeprecated(&admin.RemoveNodeCmd{}, "docker-node-remove")
	m.RegisterDeprecated(&admin.UpdateNodeCmd{}, "docker-node-update")
	m.RegisterDeprecated(&admin.ListNodesCmd{}, "docker-node-list")
	m.RegisterDeprecated(&admin.GetNodeHealingConfigCmd{}, "docker-healing-info")
	m.RegisterDeprecated(&admin.SetNodeHealingConfigCmd{}, "docker-healing-update")
	m.RegisterDeprecated(&admin.DeleteNodeHealingConfigCmd{}, "docker-healing-delete")
	m.RegisterDeprecated(&admin.RebalanceNodeCmd{}, "containers-rebalance")
	m.RegisterDeprecated(&admin.AutoScaleRunCmd{}, "docker-autoscale-run")
	m.RegisterDeprecated(&admin.ListAutoScaleHistoryCmd{}, "docker-autoscale-list")
	m.RegisterDeprecated(&admin.AutoScaleInfoCmd{}, "docker-autoscale-info")
	m.RegisterDeprecated(&admin.AutoScaleSetRuleCmd{}, "docker-autoscale-rule-set")
	m.RegisterDeprecated(&admin.AutoScaleDeleteRuleCmd{}, "docker-autoscale-rule-remove")
	m.RegisterDeprecated(&admin.ListHealingHistoryCmd{}, "docker-healing-list")
	registerExtraCommands(m)
	return m
}

func registerExtraCommands(m *cmd.Manager) {
	for _, c := range cmd.ExtraCmds() {
		m.Register(c)
	}
}

func inDockerMachineDriverMode() bool {
	return os.Getenv(localbinary.PluginEnvKey) == localbinary.PluginEnvVal
}

func main() {
	if inDockerMachineDriverMode() {
		err := dockermachine.RunDriver(os.Getenv(localbinary.PluginEnvDriverName))
		if err != nil {
			log.Fatalf("Error running driver: %s", err)
		}
	} else {
		localbinary.CurrentBinaryIsDockerMachine = true
		name := cmd.ExtractProgramName(os.Args[0])
		m := buildManager(name)
		m.Run(os.Args[1:])
	}
}
<|endoftext|>


<commit_before>/*
 * connector.go
 *
 * Copyright 2017 Bill Zissimopoulos
 */
/*
 * This file is part of netchan.
 *
 * It is licensed under the MIT license. The full license text can be found
 * in the License.txt file at the root of this project.
 */

package netchan

import (
	"net/url"
	"reflect"
	"sync"
)

type coninfo struct {
	slist []reflect.SelectCase
	ilist []string
	elist []chan error
}

type connector struct {
	transport Transport
	conmux    sync.RWMutex
	conmap    map[Link]coninfo
	lnkmap    map[interface{}]Link
}

// NewConnector creates a new Connector that can be used to connect
// channels. It is usually sufficient to use the DefaultConnector instead.
func NewConnector(transport Transport) Connector {
	self := &connector{
		transport: transport,
		conmap:    make(map[Link]coninfo),
		lnkmap:    make(map[interface{}]Link),
	}
	transport.SetChanDecoder(self)
	transport.SetSender(self.sender)
	return self
}

func (self *connector) Connect(iuri interface{}, ichan interface{}, echan chan error) error {
	vchan := reflect.ValueOf(ichan)
	if reflect.Chan != vchan.Kind() || 0 == vchan.Type().ChanDir()&reflect.SendDir {
		panic(ErrArgumentInvalid)
	}

	var uri *url.URL
	var err error
	switch u := iuri.(type) {
	case string:
		uri, err = url.Parse(u)
		if nil != err {
			return NewErrArgument(err)
		}
	case *url.URL:
		uri = u
	case nil:
		uri = nil
	default:
		panic(ErrArgumentInvalid)
	}

	var id string
	var link Link
	if nil != uri {
		var err error
		id, link, err = self.transport.Connect(uri)
		if nil != err {
			return err
		}

		/*
		 * At this point Transport.Connect() has allocated a link and may have allocated
		 * additional resources. Ideally we would not want the connect() operation below
		 * to fail. Unfortunately there is still the possibility of getting an already
		 * connected channel, which we must treat as an error.
		 *
		 * Although we could check for that possibility before the Transport.Connect()
		 * call, to do this without race conditions we would have to hold the conmux lock
		 * over Transport.Connect() which is a potentially slow call. So we choose the
		 * least worse evil, which is to sometimes fail the connect() operation after a
		 * successful Transport.Connect().
		 */
	}

	return self.connect(id, link, vchan, echan)
}

func (self *connector) connect(id string, link Link, vchan reflect.Value, echan chan error) error {
	self.conmux.Lock()
	defer self.conmux.Unlock()

	ichan := vchan.Interface()
	oldlink, ok := self.lnkmap[ichan]
	if !ok {
		if nil == link {
			return ErrArgumentNotConnected
		}
	} else {
		if nil != link {
			return ErrArgumentConnected
		}

		link = oldlink
	}

	info := self.conmap[link]
	found := false
	for i, s := range info.slist {
		if s.Chan == vchan {
			found = true
			info.elist[i] = echan
			break
		}
	}

	if !found {
		self.addsigchan(&info)

		info.slist = append(info.slist,
			reflect.SelectCase{Dir: reflect.SelectRecv, Chan: vchan})
		info.ilist = append(info.ilist, id)
		info.elist = append(info.elist, echan)

		self.conmap[link] = info
		info.slist[0].Chan.Send(reflect.ValueOf(struct{}{}))

		link.Open()
	} else {
		self.conmap[link] = info
		info.slist[0].Chan.Send(reflect.ValueOf(struct{}{}))
	}

	self.lnkmap[ichan] = link

	return nil
}

func (self *connector) disconnect(link Link, vchan reflect.Value) {
	self.conmux.Lock()
	defer self.conmux.Unlock()

	info := self.conmap[link]
	for i, s := range info.slist {
		if s.Chan == vchan {
			info.slist = append(info.slist[:i], info.slist[i+1:]...)
			info.ilist = append(info.ilist[:i], info.ilist[i+1:]...)
			info.elist = append(info.elist[:i], info.elist[i+1:]...)

			self.conmap[link] = info

			return
		}
	}
}

func (self *connector) addsigchan(info *coninfo) {
	if nil == info.slist {
		info.slist = append(info.slist,
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(make(chan struct{}, 0x7fffffff)),
			})
		info.ilist = append(info.ilist, "")
		info.elist = append(info.elist, nil)
	}
}

func (self *connector) sender(link Link) error {
	self.conmux.Lock()
	info := self.conmap[link]
	self.addsigchan(&info)
	self.conmap[link] = info
	self.conmux.Unlock()

outer:
	for {
		// make a copy so that we can safely use it outside the read lock
		self.conmux.RLock()
		info := self.conmap[link]
		slist := append([]reflect.SelectCase(nil), info.slist...)
		ilist := append([]string(nil), info.ilist...)
		elist := append([]chan error(nil), info.elist...)
		self.conmux.RUnlock()

		for {
			i, vmsg, ok := reflect.Select(slist)
			if 0 == i {
				sigchan := slist[0].Chan.Interface().(chan struct{})
			drain:
				for {
					select {
					case <-sigchan:
					default:
						break drain
					}
				}
				continue outer
			}
			if !ok {
				self.disconnect(link, slist[i].Chan)
				continue outer
			}

			err := link.Send(ilist[i], vmsg)
			if nil != err {
				if nil != elist[i] {
					if e, ok := err.(errArgs); ok {
						e.args(slist[i].Chan.Interface())
					}

					func() {
						defer func() {
							recover()
						}()
						elist[i] <- err
					}()
				}

				if _, ok := err.(*ErrTransport); ok {
					return err
				}
			}
		}
	}
}

func (self *connector) ChanDecode(link Link, ichan interface{}, buf []byte) error {
	var w weakref
	copy(w[:], buf)
	id := refEncode(w)

	v := reflect.ValueOf(ichan).Elem()
	u := reflect.MakeChan(v.Type(), 1)
	v.Set(u)

	/*
	 * connect() may only return "chan is already connected" error,
	 * which cannot happen in this scenario (because we always create
	 * new channels with reflect.MakeChan()).
	 */

	self.connect(id, link, u, nil)

	return nil
}

// DefaultConnector is the default Connector of the running process.
// Instead of DefaultConnector you can use the Connect function.
var DefaultConnector Connector = NewConnector(DefaultTransport)

// Connect connects a local channel to a remotely published channel.
// After the connection is established, the connected channel may be
// used to send messages to the remote channel.
//
// Remotely published channels may be addressed by URI's. The URI
// syntax depends on the underlying transport. For the default TCP
// transport an address has the syntax: tcp://HOST[:PORT]/ID
//
// The uri parameter contains the URI and can be of type string or
// *url.URL. An error channel (of type chan error) may also be
// specified. This error channel will receive transport errors, etc.
// related to the connected channel.
//
// It is also possible to associate a new error channel with an
// already connected channel. For this purpose use a nil uri and
// the new error channel to associate with the connected channel.
//
// To disconnect a connected channel simply close it.
//
// Connect connects a channel using the DefaultConnector.
func Connect(uri interface{}, ichan interface{}, echan chan error) error {
	return DefaultConnector.Connect(uri, ichan, echan)
}
<commit_msg>connector: issue transport.Listen on Connect to allow for implicit channels<commit_after>/*
 * connector.go
 *
 * Copyright 2017 Bill Zissimopoulos
 */
/*
 * This file is part of netchan.
 *
 * It is licensed under the MIT license. The full license text can be found
 * in the License.txt file at the root of this project.
 */

package netchan

import (
	"net/url"
	"reflect"
	"sync"
)

type coninfo struct {
	slist []reflect.SelectCase
	ilist []string
	elist []chan error
}

type connector struct {
	transport Transport
	conmux    sync.RWMutex
	conmap    map[Link]coninfo
	lnkmap    map[interface{}]Link
}

// NewConnector creates a new Connector that can be used to connect
// channels. It is usually sufficient to use the DefaultConnector instead.
func NewConnector(transport Transport) Connector {
	self := &connector{
		transport: transport,
		conmap:    make(map[Link]coninfo),
		lnkmap:    make(map[interface{}]Link),
	}
	transport.SetChanDecoder(self)
	transport.SetSender(self.sender)
	return self
}

func (self *connector) Connect(iuri interface{}, ichan interface{}, echan chan error) error {
	vchan := reflect.ValueOf(ichan)
	if reflect.Chan != vchan.Kind() || 0 == vchan.Type().ChanDir()&reflect.SendDir {
		panic(ErrArgumentInvalid)
	}

	var uri *url.URL
	var err error
	switch u := iuri.(type) {
	case string:
		uri, err = url.Parse(u)
		if nil != err {
			return NewErrArgument(err)
		}
	case *url.URL:
		uri = u
	case nil:
		uri = nil
	default:
		panic(ErrArgumentInvalid)
	}

	var id string
	var link Link
	if nil != uri {
		var err error
		err = self.transport.Listen()
		if nil != err {
			return err
		}

		id, link, err = self.transport.Connect(uri)
		if nil != err {
			return err
		}

		/*
		 * At this point Transport.Connect() has allocated a link and may have allocated
		 * additional resources. Ideally we would not want the connect() operation below
		 * to fail. Unfortunately there is still the possibility of getting an already
		 * connected channel, which we must treat as an error.
		 *
		 * Although we could check for that possibility before the Transport.Connect()
		 * call, to do this without race conditions we would have to hold the conmux lock
		 * over Transport.Connect() which is a potentially slow call. So we choose the
		 * least worse evil, which is to sometimes fail the connect() operation after a
		 * successful Transport.Connect().
		 */
	}

	return self.connect(id, link, vchan, echan)
}

func (self *connector) connect(id string, link Link, vchan reflect.Value, echan chan error) error {
	self.conmux.Lock()
	defer self.conmux.Unlock()

	ichan := vchan.Interface()
	oldlink, ok := self.lnkmap[ichan]
	if !ok {
		if nil == link {
			return ErrArgumentNotConnected
		}
	} else {
		if nil != link {
			return ErrArgumentConnected
		}

		link = oldlink
	}

	info := self.conmap[link]
	found := false
	for i, s := range info.slist {
		if s.Chan == vchan {
			found = true
			info.elist[i] = echan
			break
		}
	}

	if !found {
		self.addsigchan(&info)

		info.slist = append(info.slist,
			reflect.SelectCase{Dir: reflect.SelectRecv, Chan: vchan})
		info.ilist = append(info.ilist, id)
		info.elist = append(info.elist, echan)

		self.conmap[link] = info
		info.slist[0].Chan.Send(reflect.ValueOf(struct{}{}))

		link.Open()
	} else {
		self.conmap[link] = info
		info.slist[0].Chan.Send(reflect.ValueOf(struct{}{}))
	}

	self.lnkmap[ichan] = link

	return nil
}

func (self *connector) disconnect(link Link, vchan reflect.Value) {
	self.conmux.Lock()
	defer self.conmux.Unlock()

	info := self.conmap[link]
	for i, s := range info.slist {
		if s.Chan == vchan {
			info.slist = append(info.slist[:i], info.slist[i+1:]...)
			info.ilist = append(info.ilist[:i], info.ilist[i+1:]...)
			info.elist = append(info.elist[:i], info.elist[i+1:]...)

			self.conmap[link] = info

			return
		}
	}
}

func (self *connector) addsigchan(info *coninfo) {
	if nil == info.slist {
		info.slist = append(info.slist,
			reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(make(chan struct{}, 0x7fffffff)),
			})
		info.ilist = append(info.ilist, "")
		info.elist = append(info.elist, nil)
	}
}

func (self *connector) sender(link Link) error {
	self.conmux.Lock()
	info := self.conmap[link]
	self.addsigchan(&info)
	self.conmap[link] = info
	self.conmux.Unlock()

outer:
	for {
		// make a copy so that we can safely use it outside the read lock
		self.conmux.RLock()
		info := self.conmap[link]
		slist := append([]reflect.SelectCase(nil), info.slist...)
		ilist := append([]string(nil), info.ilist...)
		elist := append([]chan error(nil), info.elist...)
		self.conmux.RUnlock()

		for {
			i, vmsg, ok := reflect.Select(slist)
			if 0 == i {
				sigchan := slist[0].Chan.Interface().(chan struct{})
			drain:
				for {
					select {
					case <-sigchan:
					default:
						break drain
					}
				}
				continue outer
			}
			if !ok {
				self.disconnect(link, slist[i].Chan)
				continue outer
			}

			err := link.Send(ilist[i], vmsg)
			if nil != err {
				if nil != elist[i] {
					if e, ok := err.(errArgs); ok {
						e.args(slist[i].Chan.Interface())
					}

					func() {
						defer func() {
							recover()
						}()
						elist[i] <- err
					}()
				}

				if _, ok := err.(*ErrTransport); ok {
					return err
				}
			}
		}
	}
}

func (self *connector) ChanDecode(link Link, ichan interface{}, buf []byte) error {
	var w weakref
	copy(w[:], buf)
	id := refEncode(w)

	v := reflect.ValueOf(ichan).Elem()
	u := reflect.MakeChan(v.Type(), 1)
	v.Set(u)

	/*
	 * connect() may only return "chan is already connected" error,
	 * which cannot happen in this scenario (because we always create
	 * new channels with reflect.MakeChan()).
	 */

	self.connect(id, link, u, nil)

	return nil
}

// DefaultConnector is the default Connector of the running process.
// Instead of DefaultConnector you can use the Connect function.
var DefaultConnector Connector = NewConnector(DefaultTransport)

// Connect connects a local channel to a remotely published channel.
// After the connection is established, the connected channel may be
// used to send messages to the remote channel.
//
// Remotely published channels may be addressed by URI's. The URI
// syntax depends on the underlying transport. For the default TCP
// transport an address has the syntax: tcp://HOST[:PORT]/ID
//
// The uri parameter contains the URI and can be of type string or
// *url.URL. An error channel (of type chan error) may also be
// specified. This error channel will receive transport errors, etc.
// related to the connected channel.
//
// It is also possible to associate a new error channel with an
// already connected channel. For this purpose use a nil uri and
// the new error channel to associate with the connected channel.
//
// To disconnect a connected channel simply close it.
//
// Connect connects a channel using the DefaultConnector.
func Connect(uri interface{}, ichan interface{}, echan chan error) error {
	return DefaultConnector.Connect(uri, ichan, echan)
}
<|endoftext|>


<commit_before>package integration_tests_test

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"testing"
)

const (
	infoLog    = "this is a info log line"
	warningLog = "this is a warning log line"
	errorLog   = "this is a error log line"
	fatalLog   = "this is a fatal log line"
)

// res is a type alias to a slice of pointers to regular expressions.
type res = []*regexp.Regexp

var (
	infoLogRE    = regexp.MustCompile(regexp.QuoteMeta(infoLog))
	warningLogRE = regexp.MustCompile(regexp.QuoteMeta(warningLog))
	errorLogRE   = regexp.MustCompile(regexp.QuoteMeta(errorLog))
	fatalLogRE   = regexp.MustCompile(regexp.QuoteMeta(fatalLog))

	stackTraceRE = regexp.MustCompile(`\ngoroutine \d+ \[[^]]+\]:\n`)

	allLogREs = res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE, stackTraceRE}

	defaultExpectedInDirREs = map[int]res{
		0: {stackTraceRE, fatalLogRE, errorLogRE, warningLogRE, infoLogRE},
		1: {stackTraceRE, fatalLogRE, errorLogRE, warningLogRE},
		2: {stackTraceRE, fatalLogRE, errorLogRE},
		3: {stackTraceRE, fatalLogRE},
	}

	defaultNotExpectedInDirREs = map[int]res{
		0: {},
		1: {infoLogRE},
		2: {infoLogRE, warningLogRE},
		3: {infoLogRE, warningLogRE, errorLogRE},
	}
)

func TestDestinationsWithDifferentFlags(t *testing.T) {
	tests := map[string]struct {
		// logfile states if the flag -log_file should be set
		logfile bool
		// logdir states if the flag -log_dir should be set
		logdir bool
		// flags is for additional flags to pass to the klog'ed executable
		flags []string

		// expectedLogFile states if we generally expect the log file to exist.
		// If this is not set, we expect the file not to exist and will error if it
		// does.
		expectedLogFile bool
		// expectedLogDir states if we generally expect the log files in the log
		// dir to exist.
		// If this is not set, we expect the log files in the log dir not to exist and
		// will error if they do.
		expectedLogDir bool

		// expectedOnStderr is a list of REs we expect to find on stderr
		expectedOnStderr res
		// notExpectedOnStderr is a list of REs that we must not find on stderr
		notExpectedOnStderr res
		// expectedInFile is a list of REs we expect to find in the log file
		expectedInFile res
		// notExpectedInFile is a list of REs we must not find in the log file
		notExpectedInFile res

		// expectedInDir is a list of REs we expect to find in the log files in the
		// log dir, specified by log severity (0 = warning, 1 = info, ...)
		expectedInDir map[int]res
		// notExpectedInDir is a list of REs we must not find in the log files in
		// the log dir, specified by log severity (0 = warning, 1 = info, ...)
		notExpectedInDir map[int]res
	}{
		"default flags": {
			// Everything, including the trace on fatal, goes to stderr

			expectedOnStderr: res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE},
		},
		"everything disabled": {
			// Nothing, including the trace on fatal, is showing anywhere

			flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			notExpectedOnStderr: allLogREs,
		},
		"everything disabled but low stderrthreshold": {
			// Everything above -stderrthreshold, including the trace on fatal, will
			// be logged to stderr, even if we set -logtostderr to false.

			flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1"},

			expectedOnStderr:    res{warningLogRE, errorLogRE, stackTraceRE},
			notExpectedOnStderr: res{infoLogRE},
		},
		"with logtostderr only": {
			// Everything, including the trace on fatal, goes to stderr

			flags: []string{"-logtostderr=true", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedOnStderr: res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE},
		},
		"with log file only": {
			// Everything, including the trace on fatal, goes to the single log file

			logfile: true,
			flags:   []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedLogFile: true,

			notExpectedOnStderr: allLogREs,
			expectedInFile:      allLogREs,
		},
		"with log dir only": {
			// Everything, including the trace on fatal, goes to the log files in the log dir

			logdir: true,
			flags:  []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedLogDir: true,

			notExpectedOnStderr: allLogREs,
			expectedInDir:       defaultExpectedInDirREs,
			notExpectedInDir:    defaultNotExpectedInDirREs,
		},
		"with log dir and logtostderr": {
			// Everything, including the trace on fatal, goes to stderr. The -log_dir is
			// ignored, nothing goes to the log files in the log dir.

			logdir: true,
			flags:  []string{"-logtostderr=true", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedOnStderr: res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE},
		},
		"with log file and log dir": {
			// Everything, including the trace on fatal, goes to the single log file.
			// The -log_dir is ignored, nothing goes to the log file in the log dir.

			logdir:  true,
			logfile: true,
			flags:   []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedLogFile: true,

			notExpectedOnStderr: allLogREs,
			expectedInFile:      allLogREs,
		},
		"with log file and alsologtostderr": {
			// Everything, including the trace on fatal, goes to the single log file
			// AND to stderr.

			flags:   []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000"},
			logfile: true,

			expectedLogFile: true,

			expectedOnStderr: allLogREs,
			expectedInFile:   allLogREs,
		},
		"with log dir and alsologtostderr": {
			// Everything, including the trace on fatal, goes to the log file in the
			// log dir AND to stderr.

			logdir: true,
			flags:  []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000"},

			expectedLogDir: true,

			expectedOnStderr: allLogREs,
			expectedInDir:    defaultExpectedInDirREs,
			notExpectedInDir: defaultNotExpectedInDirREs,
		},
	}

	for tcName, tc := range tests {
		tc := tc
		t.Run(tcName, func(t *testing.T) {
			t.Parallel()
			withTmpDir(t, func(logdir string) {
				// :: Setup
				flags := tc.flags
				stderr := &bytes.Buffer{}
				logfile := filepath.Join(logdir, "the_single_log_file") // /some/tmp/dir/the_single_log_file

				if tc.logfile {
					flags = append(flags, "-log_file="+logfile)
				}
				if tc.logdir {
					flags = append(flags, "-log_dir="+logdir)
				}

				// :: Execute
				klogRun(t, flags, stderr)

				// :: Assert
				// check stderr
				checkForLogs(t, tc.expectedOnStderr, tc.notExpectedOnStderr, stderr.String(), "stderr")

				// check log_file
				if tc.expectedLogFile {
					content := getFileContent(t, logfile)
					checkForLogs(t, tc.expectedInFile, tc.notExpectedInFile, content, "logfile")
				} else {
					assertFileIsAbsent(t, logfile)
				}

				// check files in log_dir
				for level, file := range logFileName {
					logfile := filepath.Join(logdir, file) // /some/tmp/dir/main.WARNING
					if tc.expectedLogDir {
						content := getFileContent(t, logfile)
						checkForLogs(t, tc.expectedInDir[level], tc.notExpectedInDir[level], content, "logfile["+file+"]")
					} else {
						assertFileIsAbsent(t, logfile)
					}
				}
			})
		})
	}
}

const klogExampleGoFile = "./internal/main.go"

// klogRun spawns a simple executable that uses klog, to later inspect its
// stderr and potentially created log files
func klogRun(t *testing.T, flags []string, stderr io.Writer) {
	callFlags := []string{"run", klogExampleGoFile}
	callFlags = append(callFlags, flags...)

	cmd := exec.Command("go", callFlags...)
	cmd.Stderr = stderr
	cmd.Env = append(os.Environ(),
		"KLOG_INFO_LOG="+infoLog,
		"KLOG_WARNING_LOG="+warningLog,
		"KLOG_ERROR_LOG="+errorLog,
		"KLOG_FATAL_LOG="+fatalLog,
	)

	err := cmd.Run()

	if _, ok := err.(*exec.ExitError); !ok {
		t.Fatalf("Run failed: %v", err)
	}
}

var logFileName = map[int]string{
	0: "main.INFO",
	1: "main.WARNING",
	2: "main.ERROR",
	3: "main.FATAL",
}

func getFileContent(t *testing.T, filePath string) string {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		t.Errorf("Could not read file '%s': %v", filePath, err)
	}
	return string(content)
}

func assertFileIsAbsent(t *testing.T, filePath string) {
	if _, err := os.Stat(filePath); !os.IsNotExist(err) {
		t.Errorf("Expected file '%s' not to exist", filePath)
	}
}

func checkForLogs(t *testing.T, expected, disallowed res, content, name string) {
	for _, re := range expected {
		checkExpected(t, true, name, content, re)
	}
	for _, re := range disallowed {
		checkExpected(t, false, name, content, re)
	}
}

func checkExpected(t *testing.T, expected bool, where string, haystack string, needle *regexp.Regexp) {
	found := needle.MatchString(haystack)

	if expected && !found {
		t.Errorf("Expected to find '%s' in %s", needle, where)
	}
	if !expected && found {
		t.Errorf("Expected not to find '%s' in %s", needle, where)
	}
}

func withTmpDir(t *testing.T, f func(string)) {
	tmpDir, err := ioutil.TempDir("", "klog_e2e_")
	if err != nil {
		t.Fatalf("Could not create temp directory: %v", err)
	}
	defer func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			t.Fatalf("Could not remove temp directory '%s': %v", tmpDir, err)
		}
	}()

	f(tmpDir)
}
<commit_msg>Update tests for new behaviour<commit_after>package integration_tests_test

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"testing"
)

const (
	infoLog    = "this is a info log line"
	warningLog = "this is a warning log line"
	errorLog   = "this is a error log line"
	fatalLog   = "this is a fatal log line"
)

// res is a type alias to a slice of pointers to regular expressions.
type res = []*regexp.Regexp

var (
	infoLogRE    = regexp.MustCompile(regexp.QuoteMeta(infoLog))
	warningLogRE = regexp.MustCompile(regexp.QuoteMeta(warningLog))
	errorLogRE   = regexp.MustCompile(regexp.QuoteMeta(errorLog))
	fatalLogRE   = regexp.MustCompile(regexp.QuoteMeta(fatalLog))

	stackTraceRE = regexp.MustCompile(`\ngoroutine \d+ \[[^]]+\]:\n`)

	allLogREs = res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE, stackTraceRE}

	defaultExpectedInDirREs = map[int]res{
		0: {stackTraceRE, fatalLogRE, errorLogRE, warningLogRE, infoLogRE},
		1: {stackTraceRE, fatalLogRE, errorLogRE, warningLogRE},
		2: {stackTraceRE, fatalLogRE, errorLogRE},
		3: {stackTraceRE, fatalLogRE},
	}

	defaultNotExpectedInDirREs = map[int]res{
		0: {},
		1: {infoLogRE},
		2: {infoLogRE, warningLogRE},
		3: {infoLogRE, warningLogRE, errorLogRE},
	}
)

func TestDestinationsWithDifferentFlags(t *testing.T) {
	tests := map[string]struct {
		// logfile states if the flag -log_file should be set
		logfile bool
		// logdir states if the flag -log_dir should be set
		logdir bool
		// flags is for additional flags to pass to the klog'ed executable
		flags []string

		// expectedLogFile states if we generally expect the log file to exist.
		// If this is not set, we expect the file not to exist and will error if it
		// does.
		expectedLogFile bool
		// expectedLogDir states if we generally expect the log files in the log
		// dir to exist.
		// If this is not set, we expect the log files in the log dir not to exist and
		// will error if they do.
		expectedLogDir bool

		// expectedOnStderr is a list of REs we expect to find on stderr
		expectedOnStderr res
		// notExpectedOnStderr is a list of REs that we must not find on stderr
		notExpectedOnStderr res
		// expectedInFile is a list of REs we expect to find in the log file
		expectedInFile res
		// notExpectedInFile is a list of REs we must not find in the log file
		notExpectedInFile res

		// expectedInDir is a list of REs we expect to find in the log files in the
		// log dir, specified by log severity (0 = warning, 1 = info, ...)
		expectedInDir map[int]res
		// notExpectedInDir is a list of REs we must not find in the log files in
		// the log dir, specified by log severity (0 = warning, 1 = info, ...)
		notExpectedInDir map[int]res
	}{
		"default flags": {
			// Everything, including the trace on fatal, goes to stderr

			expectedOnStderr: allLogREs,
		},
		"everything disabled": {
			// Nothing, including the trace on fatal, is showing anywhere

			flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			notExpectedOnStderr: allLogREs,
		},
		"everything disabled but low stderrthreshold": {
			// Everything above -stderrthreshold, including the trace on fatal, will
			// be logged to stderr, even if we set -logtostderr to false.

			flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1"},

			expectedOnStderr:    res{warningLogRE, errorLogRE, stackTraceRE},
			notExpectedOnStderr: res{infoLogRE},
		},
		"with logtostderr only": {
			// Everything, including the trace on fatal, goes to stderr

			flags: []string{"-logtostderr=true", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedOnStderr: allLogREs,
		},
		"with log file only": {
			// Everything, including the trace on fatal, goes to the single log file

			logfile: true,
			flags:   []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedLogFile: true,

			notExpectedOnStderr: allLogREs,
			expectedInFile:      allLogREs,
		},
		"with log dir only": {
			// Everything, including the trace on fatal, goes to the log files in the log dir

			logdir: true,
			flags:  []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedLogDir: true,

			notExpectedOnStderr: allLogREs,
			expectedInDir:       defaultExpectedInDirREs,
			notExpectedInDir:    defaultNotExpectedInDirREs,
		},
		"with log dir and logtostderr": {
			// Everything, including the trace on fatal, goes to stderr. The -log_dir is
			// ignored, nothing goes to the log files in the log dir.

			logdir: true,
			flags:  []string{"-logtostderr=true", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedOnStderr: allLogREs,
		},
		"with log file and log dir": {
			// Everything, including the trace on fatal, goes to the single log file.
			// The -log_dir is ignored, nothing goes to the log file in the log dir.

			logdir:  true,
			logfile: true,
			flags:   []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},

			expectedLogFile: true,

			notExpectedOnStderr: allLogREs,
			expectedInFile:      allLogREs,
		},
		"with log file and alsologtostderr": {
			// Everything, including the trace on fatal, goes to the single log file
			// AND to stderr.

			flags:   []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000"},
			logfile: true,

			expectedLogFile: true,

			expectedOnStderr: allLogREs,
			expectedInFile:   allLogREs,
		},
		"with log dir and alsologtostderr": {
			// Everything, including the trace on fatal, goes to the log file in the
			// log dir AND to stderr.

			logdir: true,
			flags:  []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000"},

			expectedLogDir: true,

			expectedOnStderr: allLogREs,
			expectedInDir:    defaultExpectedInDirREs,
			notExpectedInDir: defaultNotExpectedInDirREs,
		},
	}

	for tcName, tc := range tests {
		tc := tc
		t.Run(tcName, func(t *testing.T) {
			t.Parallel()
			withTmpDir(t, func(logdir string) {
				// :: Setup
				flags := tc.flags
				stderr := &bytes.Buffer{}
				logfile := filepath.Join(logdir, "the_single_log_file") // /some/tmp/dir/the_single_log_file

				if tc.logfile {
					flags = append(flags, "-log_file="+logfile)
				}
				if tc.logdir {
					flags = append(flags, "-log_dir="+logdir)
				}

				// :: Execute
				klogRun(t, flags, stderr)

				// :: Assert
				// check stderr
				checkForLogs(t, tc.expectedOnStderr, tc.notExpectedOnStderr, stderr.String(), "stderr")

				// check log_file
				if tc.expectedLogFile {
					content := getFileContent(t, logfile)
					checkForLogs(t, tc.expectedInFile, tc.notExpectedInFile, content, "logfile")
				} else {
					assertFileIsAbsent(t, logfile)
				}

				// check files in log_dir
				for level, file := range logFileName {
					logfile := filepath.Join(logdir, file) // /some/tmp/dir/main.WARNING
					if tc.expectedLogDir {
						content := getFileContent(t, logfile)
						checkForLogs(t, tc.expectedInDir[level], tc.notExpectedInDir[level], content, "logfile["+file+"]")
					} else {
						assertFileIsAbsent(t, logfile)
					}
				}
			})
		})
	}
}

const klogExampleGoFile = "./internal/main.go"

// klogRun spawns a simple executable that uses klog, to later inspect its
// stderr and potentially created log files
func klogRun(t *testing.T, flags []string, stderr io.Writer) {
	callFlags := []string{"run", klogExampleGoFile}
	callFlags = append(callFlags, flags...)

	cmd := exec.Command("go", callFlags...)
	cmd.Stderr = stderr
	cmd.Env = append(os.Environ(),
		"KLOG_INFO_LOG="+infoLog,
		"KLOG_WARNING_LOG="+warningLog,
		"KLOG_ERROR_LOG="+errorLog,
		"KLOG_FATAL_LOG="+fatalLog,
	)

	err := cmd.Run()

	if _, ok := err.(*exec.ExitError); !ok {
		t.Fatalf("Run failed: %v", err)
	}
}

var logFileName = map[int]string{
	0: "main.INFO",
	1: "main.WARNING",
	2: "main.ERROR",
	3: "main.FATAL",
}

func getFileContent(t *testing.T, filePath string) string {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		t.Errorf("Could not read file '%s': %v", filePath, err)
	}
	return string(content)
}

func assertFileIsAbsent(t *testing.T, filePath string) {
	if _, err := os.Stat(filePath); !os.IsNotExist(err) {
		t.Errorf("Expected file '%s' not to exist", filePath)
	}
}

func checkForLogs(t *testing.T, expected, disallowed res, content, name string) {
	for _, re := range expected {
		checkExpected(t, true, name, content, re)
	}
	for _, re := range disallowed {
		checkExpected(t, false, name, content, re)
	}
}

func checkExpected(t *testing.T, expected bool, where string, haystack string, needle *regexp.Regexp) {
	found := needle.MatchString(haystack)

	if expected && !found {
		t.Errorf("Expected to find '%s' in %s", needle, where)
	}
	if !expected && found {
		t.Errorf("Expected not to find '%s' in %s", needle, where)
	}
}

func withTmpDir(t *testing.T, f func(string)) {
	tmpDir, err := ioutil.TempDir("", "klog_e2e_")
	if err != nil {
		t.Fatalf("Could not create temp directory: %v", err)
	}
	defer func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			t.Fatalf("Could not remove temp directory '%s': %v", tmpDir, err)
		}
	}()

	f(tmpDir)
}
<|endoftext|>


<commit_before>// Package igo implements the machinery necessary to run a Go kernel for IPython.
// It should be installed with an "igo" command to launch the kernel.
package igo

import (
    "fmt"
    "log"
    "io"
    "io/ioutil"
    "encoding/json"
    "encoding/hex"
    "crypto/sha256"
    "crypto/hmac"
    zmq "github.com/alecthomas/gozmq"
    uuid "github.com/nu7hatch/gouuid"
    "go/token"
    "github.com/sbinet/go-eval/pkg/eval"

)

var logger *log.Logger

type MsgHeader struct  {
    Msg_id string `json:"msg_id"`
    Username string `json:"username"`
    Session string `json:"session"`
    Msg_type string `json:"msg_type"`
}

// ComposedMsg represents an entire message in a high-level structure.
type ComposedMsg struct {
    Header MsgHeader
    Parent_header MsgHeader
    Metadata map[string]interface{}
    Content interface{}
}

// ConnectionInfo stores the contents of the kernel connection file created by IPython.
type ConnectionInfo struct {
    Signature_scheme string
    Transport string
    Stdin_port int
    Control_port int
    IOPub_port int
    HB_port int
    Shell_port int
    Key string
    IP string
}

// SocketGroup holds the sockets the kernel needs to communicate with the kernel, and
// the key for message signing.
type SocketGroup struct {
    Shell_socket *zmq.Socket
    Stdin_socket *zmq.Socket
    IOPub_socket *zmq.Socket
    Key []byte
}

// PrepareSockets sets up the ZMQ sockets through which the kernel will communicate.
func PrepareSockets(conn_info ConnectionInfo) (sg SocketGroup) {
    context, _ := zmq.NewContext()
    sg.Shell_socket, _ = context.NewSocket(zmq.ROUTER)
    sg.Stdin_socket, _ = context.NewSocket(zmq.ROUTER)
    sg.IOPub_socket, _ = context.NewSocket(zmq.PUB)

    address := fmt.Sprintf("%v://%v:%%v", conn_info.Transport, conn_info.IP)

    sg.Shell_socket.Bind(fmt.Sprintf(address, conn_info.Shell_port))
    sg.Stdin_socket.Bind(fmt.Sprintf(address, conn_info.Stdin_port))
    sg.IOPub_socket.Bind(fmt.Sprintf(address, conn_info.IOPub_port))

    // Message signing key
    sg.Key = []byte(conn_info.Key)

    // Start the heartbeat device
    HB_socket, _ := context.NewSocket(zmq.REP)
    HB_socket.Bind(fmt.Sprintf(address, conn_info.HB_port))
    go zmq.Device(zmq.FORWARDER, HB_socket, HB_socket)
    return
}

// InvalidSignatureError is returned when the signature on a received message does not
// validate.
type InvalidSignatureError struct {}
func (e *InvalidSignatureError) Error() string {
    return "A message had an invalid signature"
}

// WireMsgToComposedMsg translates a multipart ZMQ messages received from a socket into
// a ComposedMsg struct and a slice of return identities. This includes verifying the
// message signature.
func WireMsgToComposedMsg(msgparts [][]byte, signkey []byte) (msg ComposedMsg,
                            identities [][]byte, err error) {
    i := 0
    for string(msgparts[i]) != "<IDS|MSG>" {
        i++
    }
    identities = msgparts[:i]
    // msgparts[i] is the delimiter

    // Validate signature
    if len(signkey) != 0 {
        mac := hmac.New(sha256.New, signkey)
        for _, msgpart := range msgparts[i+2:i+6] {
            mac.Write(msgpart)
        }
        signature := make([]byte, hex.DecodedLen(len(msgparts[i+1])))
        hex.Decode(signature, msgparts[i+1])
        if !hmac.Equal(mac.Sum(nil), signature) {
            return msg, nil, &InvalidSignatureError{}
        }
    }
    json.Unmarshal(msgparts[i+2], &msg.Header)
    json.Unmarshal(msgparts[i+3], &msg.Parent_header)
    json.Unmarshal(msgparts[i+4], &msg.Metadata)
    json.Unmarshal(msgparts[i+5], &msg.Content)
    return
}

// ToWireMsg translates a ComposedMsg into a multipart ZMQ message ready to send, and
// signs it. This does not add the return identities or the delimiter.
func (msg ComposedMsg) ToWireMsg(signkey []byte) (msgparts [][]byte) {
    msgparts = make([][]byte, 5)
    header, _ := json.Marshal(msg.Header)
    msgparts[1] = header
    parent_header, _ := json.Marshal(msg.Parent_header)
    msgparts[2] = parent_header
    if msg.Metadata == nil {
        msg.Metadata = make(map[string]interface{})
    }
    metadata, _ := json.Marshal(msg.Metadata)
    msgparts[3] = metadata
    content, _ := json.Marshal(msg.Content)
    msgparts[4] = content

    // Sign the message
    if len(signkey) != 0 {
        mac := hmac.New(sha256.New, signkey)
        for _, msgpart := range msgparts[1:] {
            mac.Write(msgpart)
        }
        msgparts[0] = make([]byte, hex.EncodedLen(mac.Size()))
        hex.Encode(msgparts[0], mac.Sum(nil))
    }
    return
}

// MsgReceipt represents a received message, its return identities, and the sockets for
// communication.
type MsgReceipt struct {
    Msg ComposedMsg
    Identities [][]byte
    Sockets SocketGroup
}

// SendResponse sends a message back to return identites of the received message.
func (receipt *MsgReceipt) SendResponse(socket *zmq.Socket, msg ComposedMsg) {
    socket.SendMultipart(receipt.Identities, zmq.SNDMORE)
    socket.Send([]byte("<IDS|MSG>"), zmq.SNDMORE)
    socket.SendMultipart(msg.ToWireMsg(receipt.Sockets.Key), 0)
    logger.Println("<--", msg.Header.Msg_type)
    logger.Println(msg.Content)
}

// HandleShellMsg responds to a message on the shell ROUTER socket.
func HandleShellMsg(receipt MsgReceipt) {
    switch receipt.Msg.Header.Msg_type {
        case "kernel_info_request":
            SendKernelInfo(receipt)
        case "execute_request":
            HandleExecuteRequest(receipt)
        default: logger.Println("Unhandled shell message:", receipt.Msg.Header.Msg_type)
    }
}

// NewMsg creates a new ComposedMsg to respond to a parent message. This includes setting
// up its headers.
func NewMsg(msg_type string, parent ComposedMsg) (msg ComposedMsg) {
    msg.Parent_header = parent.Header
    msg.Header.Session = parent.Header.Session
    msg.Header.Username = parent.Header.Username
    msg.Header.Msg_type = msg_type
    u, _ := uuid.NewV4()
    msg.Header.Msg_id = u.String()
    return
}

// KernelInfo holds information about the igo kernel, for kernel_info_reply messages.
type KernelInfo struct {
    Protocol_version []int `json:"protocol_version"`
    Language string `json:"language"`
}

// KernelStatus holds a kernel state, for status broadcast messages.
type KernelStatus struct {
    ExecutionState string `json:"execution_state"`
}

//SendKernelInfo sends a kernel_info_reply message.
func SendKernelInfo(receipt MsgReceipt) {
    reply := NewMsg("kernel_info_reply", receipt.Msg)
    reply.Content = KernelInfo{[]int{4, 0}, "go"}
    receipt.SendResponse(receipt.Sockets.Shell_socket, reply)
}

// World holds the user namespace for the REPL.
var World *eval.World
var fset *token.FileSet
// ExecCounter is incremented each time we run user code.
var ExecCounter int = 0

// RunCode runs the given user code, returning the expression value and/or an error.
func RunCode(text string) (val interface{}, err error) {
    var code eval.Code
    code, err = World.Compile(fset, text)
    if err != nil {
        return nil, err
    }
    val, err = code.Run()
    return
}

// OutputMsg holds the data for a pyout message.
type OutputMsg struct {
    Execcount int `json:"execution_count"`
    Data map[string]string `json:"data"`
    Metadata map[string]interface{} `json:"metadata"`
}

// HandleExecuteRequest runs code from an execute_request method, and sends the various
// reply messages.
func HandleExecuteRequest(receipt MsgReceipt) {
    reply := NewMsg("execute_reply", receipt.Msg)
    content := make(map[string]interface{})
    reqcontent := receipt.Msg.Content.(map[string]interface{})
    code := reqcontent["code"].(string)
    ExecCounter++
    content["execution_count"] = ExecCounter
    val, err := RunCode(code)
    if err == nil {
        content["status"] = "ok"
        content["payload"] = make([]map[string]interface{}, 0)
        content["user_variables"] = make(map[string]string)
        content["user_expressions"] = make(map[string]string)
        if val != nil {
            var out_content OutputMsg
            out := NewMsg("pyout", receipt.Msg)
            out_content.Execcount = ExecCounter
            out_content.Data = make(map[string]string)
            out_content.Data["text/plain"] = fmt.Sprint(val)
            out.Content = out_content
            receipt.SendResponse(receipt.Sockets.IOPub_socket, out)
        }
    } else {
        content["status"] = "error"
        content["ename"] = "ERROR"
        content["evalue"] = err.Error()
        content["traceback"] = []string{err.Error()}
    }
    reply.Content = content
    receipt.SendResponse(receipt.Sockets.Shell_socket, reply)
    idle := NewMsg("status", receipt.Msg)
    idle.Content = KernelStatus{"idle"}
    receipt.SendResponse(receipt.Sockets.IOPub_socket, idle)
}

// RunKernel is the main entry point to start the kernel. This is what is called by the
// igo executable.
func RunKernel(connection_file string, logwriter io.Writer) {
    logger = log.New(logwriter, "igopkg ", log.LstdFlags)
    World = eval.NewWorld()
    fset = token.NewFileSet()
    var conn_info ConnectionInfo
    bs, err := ioutil.ReadFile(connection_file)
    if err != nil {
        log.Fatalln(err)
    }
    err = json.Unmarshal(bs, &conn_info)
    if err != nil {
        log.Fatalln(err)
    }
    logger.Printf("%+v\n", conn_info)
    sockets := PrepareSockets(conn_info)

    pi := zmq.PollItems{
        zmq.PollItem{Socket: sockets.Shell_socket, Events: zmq.POLLIN},
        zmq.PollItem{Socket: sockets.Stdin_socket, Events: zmq.POLLIN},
    }
    var msgparts [][]byte
    // Message receiving loop:
    for {
        _, err = zmq.Poll(pi, -1)
        if err != nil {
            log.Fatalln(err)
        }
        switch {
        case pi[0].REvents&zmq.POLLIN != 0:
            msgparts, _ = pi[0].Socket.RecvMultipart(0)
            msg, ids, err := WireMsgToComposedMsg(msgparts, sockets.Key)
            if err != nil {
                fmt.Println(err)
                return
            }
            HandleShellMsg(MsgReceipt{msg, ids, sockets})
        case pi[1].REvents&zmq.POLLIN != 0:
            pi[1].Socket.RecvMultipart(0)
        }
    }
}
<commit_msg>Support silent flag for execute_request<commit_after>// Package igo implements the machinery necessary to run a Go kernel for IPython.
// It should be installed with an "igo" command to launch the kernel.
package igo

import (
    "fmt"
    "log"
    "io"
    "io/ioutil"
    "encoding/json"
    "encoding/hex"
    "crypto/sha256"
    "crypto/hmac"
    zmq "github.com/alecthomas/gozmq"
    uuid "github.com/nu7hatch/gouuid"
    "go/token"
    "github.com/sbinet/go-eval/pkg/eval"

)

var logger *log.Logger

type MsgHeader struct  {
    Msg_id string `json:"msg_id"`
    Username string `json:"username"`
    Session string `json:"session"`
    Msg_type string `json:"msg_type"`
}

// ComposedMsg represents an entire message in a high-level structure.
type ComposedMsg struct {
    Header MsgHeader
    Parent_header MsgHeader
    Metadata map[string]interface{}
    Content interface{}
}

// ConnectionInfo stores the contents of the kernel connection file created by IPython.
type ConnectionInfo struct {
    Signature_scheme string
    Transport string
    Stdin_port int
    Control_port int
    IOPub_port int
    HB_port int
    Shell_port int
    Key string
    IP string
}

// SocketGroup holds the sockets the kernel needs to communicate with the kernel, and
// the key for message signing.
type SocketGroup struct {
    Shell_socket *zmq.Socket
    Stdin_socket *zmq.Socket
    IOPub_socket *zmq.Socket
    Key []byte
}

// PrepareSockets sets up the ZMQ sockets through which the kernel will communicate.
func PrepareSockets(conn_info ConnectionInfo) (sg SocketGroup) {
    context, _ := zmq.NewContext()
    sg.Shell_socket, _ = context.NewSocket(zmq.ROUTER)
    sg.Stdin_socket, _ = context.NewSocket(zmq.ROUTER)
    sg.IOPub_socket, _ = context.NewSocket(zmq.PUB)

    address := fmt.Sprintf("%v://%v:%%v", conn_info.Transport, conn_info.IP)

    sg.Shell_socket.Bind(fmt.Sprintf(address, conn_info.Shell_port))
    sg.Stdin_socket.Bind(fmt.Sprintf(address, conn_info.Stdin_port))
    sg.IOPub_socket.Bind(fmt.Sprintf(address, conn_info.IOPub_port))

    // Message signing key
    sg.Key = []byte(conn_info.Key)

    // Start the heartbeat device
    HB_socket, _ := context.NewSocket(zmq.REP)
    HB_socket.Bind(fmt.Sprintf(address, conn_info.HB_port))
    go zmq.Device(zmq.FORWARDER, HB_socket, HB_socket)
    return
}

// InvalidSignatureError is returned when the signature on a received message does not
// validate.
type InvalidSignatureError struct {}
func (e *InvalidSignatureError) Error() string {
    return "A message had an invalid signature"
}

// WireMsgToComposedMsg translates a multipart ZMQ messages received from a socket into
// a ComposedMsg struct and a slice of return identities. This includes verifying the
// message signature.
func WireMsgToComposedMsg(msgparts [][]byte, signkey []byte) (msg ComposedMsg,
                            identities [][]byte, err error) {
    i := 0
    for string(msgparts[i]) != "<IDS|MSG>" {
        i++
    }
    identities = msgparts[:i]
    // msgparts[i] is the delimiter

    // Validate signature
    if len(signkey) != 0 {
        mac := hmac.New(sha256.New, signkey)
        for _, msgpart := range msgparts[i+2:i+6] {
            mac.Write(msgpart)
        }
        signature := make([]byte, hex.DecodedLen(len(msgparts[i+1])))
        hex.Decode(signature, msgparts[i+1])
        if !hmac.Equal(mac.Sum(nil), signature) {
            return msg, nil, &InvalidSignatureError{}
        }
    }
    json.Unmarshal(msgparts[i+2], &msg.Header)
    json.Unmarshal(msgparts[i+3], &msg.Parent_header)
    json.Unmarshal(msgparts[i+4], &msg.Metadata)
    json.Unmarshal(msgparts[i+5], &msg.Content)
    return
}

// ToWireMsg translates a ComposedMsg into a multipart ZMQ message ready to send, and
// signs it. This does not add the return identities or the delimiter.
func (msg ComposedMsg) ToWireMsg(signkey []byte) (msgparts [][]byte) {
    msgparts = make([][]byte, 5)
    header, _ := json.Marshal(msg.Header)
    msgparts[1] = header
    parent_header, _ := json.Marshal(msg.Parent_header)
    msgparts[2] = parent_header
    if msg.Metadata == nil {
        msg.Metadata = make(map[string]interface{})
    }
    metadata, _ := json.Marshal(msg.Metadata)
    msgparts[3] = metadata
    content, _ := json.Marshal(msg.Content)
    msgparts[4] = content

    // Sign the message
    if len(signkey) != 0 {
        mac := hmac.New(sha256.New, signkey)
        for _, msgpart := range msgparts[1:] {
            mac.Write(msgpart)
        }
        msgparts[0] = make([]byte, hex.EncodedLen(mac.Size()))
        hex.Encode(msgparts[0], mac.Sum(nil))
    }
    return
}

// MsgReceipt represents a received message, its return identities, and the sockets for
// communication.
type MsgReceipt struct {
    Msg ComposedMsg
    Identities [][]byte
    Sockets SocketGroup
}

// SendResponse sends a message back to return identites of the received message.
func (receipt *MsgReceipt) SendResponse(socket *zmq.Socket, msg ComposedMsg) {
    socket.SendMultipart(receipt.Identities, zmq.SNDMORE)
    socket.Send([]byte("<IDS|MSG>"), zmq.SNDMORE)
    socket.SendMultipart(msg.ToWireMsg(receipt.Sockets.Key), 0)
    logger.Println("<--", msg.Header.Msg_type)
    logger.Println(msg.Content)
}

// HandleShellMsg responds to a message on the shell ROUTER socket.
func HandleShellMsg(receipt MsgReceipt) {
    switch receipt.Msg.Header.Msg_type {
        case "kernel_info_request":
            SendKernelInfo(receipt)
        case "execute_request":
            HandleExecuteRequest(receipt)
        default: logger.Println("Unhandled shell message:", receipt.Msg.Header.Msg_type)
    }
}

// NewMsg creates a new ComposedMsg to respond to a parent message. This includes setting
// up its headers.
func NewMsg(msg_type string, parent ComposedMsg) (msg ComposedMsg) {
    msg.Parent_header = parent.Header
    msg.Header.Session = parent.Header.Session
    msg.Header.Username = parent.Header.Username
    msg.Header.Msg_type = msg_type
    u, _ := uuid.NewV4()
    msg.Header.Msg_id = u.String()
    return
}

// KernelInfo holds information about the igo kernel, for kernel_info_reply messages.
type KernelInfo struct {
    Protocol_version []int `json:"protocol_version"`
    Language string `json:"language"`
}

// KernelStatus holds a kernel state, for status broadcast messages.
type KernelStatus struct {
    ExecutionState string `json:"execution_state"`
}

//SendKernelInfo sends a kernel_info_reply message.
func SendKernelInfo(receipt MsgReceipt) {
    reply := NewMsg("kernel_info_reply", receipt.Msg)
    reply.Content = KernelInfo{[]int{4, 0}, "go"}
    receipt.SendResponse(receipt.Sockets.Shell_socket, reply)
}

// World holds the user namespace for the REPL.
var World *eval.World
var fset *token.FileSet
// ExecCounter is incremented each time we run user code.
var ExecCounter int = 0

// RunCode runs the given user code, returning the expression value and/or an error.
func RunCode(text string) (val interface{}, err error) {
    var code eval.Code
    code, err = World.Compile(fset, text)
    if err != nil {
        return nil, err
    }
    val, err = code.Run()
    return
}

// OutputMsg holds the data for a pyout message.
type OutputMsg struct {
    Execcount int `json:"execution_count"`
    Data map[string]string `json:"data"`
    Metadata map[string]interface{} `json:"metadata"`
}

// HandleExecuteRequest runs code from an execute_request method, and sends the various
// reply messages.
func HandleExecuteRequest(receipt MsgReceipt) {
    reply := NewMsg("execute_reply", receipt.Msg)
    content := make(map[string]interface{})
    reqcontent := receipt.Msg.Content.(map[string]interface{})
    code := reqcontent["code"].(string)
    silent := reqcontent["silent"].(bool)
    if !silent {
        ExecCounter++
    }
    content["execution_count"] = ExecCounter
    val, err := RunCode(code)
    if err == nil {
        content["status"] = "ok"
        content["payload"] = make([]map[string]interface{}, 0)
        content["user_variables"] = make(map[string]string)
        content["user_expressions"] = make(map[string]string)
        if (val != nil) && !silent {
            var out_content OutputMsg
            out := NewMsg("pyout", receipt.Msg)
            out_content.Execcount = ExecCounter
            out_content.Data = make(map[string]string)
            out_content.Data["text/plain"] = fmt.Sprint(val)
            out.Content = out_content
            receipt.SendResponse(receipt.Sockets.IOPub_socket, out)
        }
    } else {
        content["status"] = "error"
        content["ename"] = "ERROR"
        content["evalue"] = err.Error()
        content["traceback"] = []string{err.Error()}
    }
    reply.Content = content
    receipt.SendResponse(receipt.Sockets.Shell_socket, reply)
    idle := NewMsg("status", receipt.Msg)
    idle.Content = KernelStatus{"idle"}
    receipt.SendResponse(receipt.Sockets.IOPub_socket, idle)
}

// RunKernel is the main entry point to start the kernel. This is what is called by the
// igo executable.
func RunKernel(connection_file string, logwriter io.Writer) {
    logger = log.New(logwriter, "igopkg ", log.LstdFlags)
    World = eval.NewWorld()
    fset = token.NewFileSet()
    var conn_info ConnectionInfo
    bs, err := ioutil.ReadFile(connection_file)
    if err != nil {
        log.Fatalln(err)
    }
    err = json.Unmarshal(bs, &conn_info)
    if err != nil {
        log.Fatalln(err)
    }
    logger.Printf("%+v\n", conn_info)
    sockets := PrepareSockets(conn_info)

    pi := zmq.PollItems{
        zmq.PollItem{Socket: sockets.Shell_socket, Events: zmq.POLLIN},
        zmq.PollItem{Socket: sockets.Stdin_socket, Events: zmq.POLLIN},
    }
    var msgparts [][]byte
    // Message receiving loop:
    for {
        _, err = zmq.Poll(pi, -1)
        if err != nil {
            log.Fatalln(err)
        }
        switch {
        case pi[0].REvents&zmq.POLLIN != 0:
            msgparts, _ = pi[0].Socket.RecvMultipart(0)
            msg, ids, err := WireMsgToComposedMsg(msgparts, sockets.Key)
            if err != nil {
                fmt.Println(err)
                return
            }
            HandleShellMsg(MsgReceipt{msg, ids, sockets})
        case pi[1].REvents&zmq.POLLIN != 0:
            pi[1].Socket.RecvMultipart(0)
        }
    }
}
<|endoftext|>


<commit_before>package main

import "strings"

const textEmailTemplate = `%s

Cheers!
https://www.onecontact.link/

Click on the following link to unsubscribe from any future messages:
%%unsubscribe_url%%
`

var htmlEmailTemplate = strings.Replace(strings.Replace(`
<!DOCTYPE HTML>

<meta name="viewport" content="width=device-width, initial-scale=1">

OneContact.Link

__s__

Cheers,
OneContactLink

      Home ·
      Manage

Unsubscribe from these emails.

`, "%", "%%", -1), "__s__", "%s", -1)
<commit_msg>fix email header link<commit_after>package main

import "strings"

const textEmailTemplate = `%s

Cheers!
https://www.onecontact.link/

Click on the following link to unsubscribe from any future messages:
%%unsubscribe_url%%
`

var htmlEmailTemplate = strings.Replace(strings.Replace(`
<!DOCTYPE HTML>

<meta name="viewport" content="width=device-width, initial-scale=1">

OneContact.Link

__s__

Cheers,
OneContactLink

      Home ·
      Manage

Unsubscribe from these emails.

`, "%", "%%", -1), "__s__", "%s", -1)
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package buffer

import (
	"fmt"
	"log"
	"reflect"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
)

const outHeaderSize = unsafe.Sizeof(fusekernel.OutHeader{})

// We size out messages to be large enough to hold a header for the response
// plus the largest read that may come in.
const outMessageSize = outHeaderSize + MaxReadSize

// OutMessage provides a mechanism for constructing a single contiguous fuse
// message from multiple segments, where the first segment is always a
// fusekernel.OutHeader message.
//
// Must be initialized with Reset.
type OutMessage struct {
	offset  uintptr
	storage [outMessageSize]byte
}

// Make sure alignment works out correctly, at least for the header.
func init() {
	a := unsafe.Alignof(OutMessage{})
	o := unsafe.Offsetof(OutMessage{}.storage)
	e := unsafe.Alignof(fusekernel.OutHeader{})

	if a%e != 0 || o%e != 0 {
		log.Panicf("Bad alignment or offset: %d, %d, need %d", a, o, e)
	}
}

// Reset the message so that it is ready to be used again. Afterward, the
// contents are solely a zeroed header.
func (m *OutMessage) Reset() {
	m.offset = outHeaderSize
	memclr(unsafe.Pointer(&m.storage), outHeaderSize)
}

// Return a pointer to the header at the start of the message.
func (b *OutMessage) OutHeader() (h *fusekernel.OutHeader) {
	h = (*fusekernel.OutHeader)(unsafe.Pointer(&b.storage))
	return
}

// Grow the buffer by the supplied number of bytes, returning a pointer to the
// start of the new segment, which is zeroed. If there is no space left, return
// the nil pointer.
func (b *OutMessage) Grow(size uintptr) (p unsafe.Pointer) {
	p = b.GrowNoZero(size)
	if p != nil {
		memclr(p, size)
	}

	return
}

// Equivalent to Grow, except the new segment is not zeroed. Use with caution!
func (b *OutMessage) GrowNoZero(size uintptr) (p unsafe.Pointer) {
	if outMessageSize-b.offset < size {
		return
	}

	p = unsafe.Pointer(uintptr(unsafe.Pointer(&b.storage)) + b.offset)
	b.offset += size

	return
}

// Equivalent to growing by the length of p, then copying p over the new
// segment. Panics if there is not enough room available.
func (b *OutMessage) Append(src []byte) {
	p := b.GrowNoZero(uintptr(len(src)))
	if p == nil {
		panic(fmt.Sprintf("Can't grow %d bytes", len(src)))
	}

	sh := (*reflect.SliceHeader)(unsafe.Pointer(&src))
	memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len))

	return
}

// Equivalent to growing by the length of s, then copying s over the new
// segment. Panics if there is not enough room available.
func (b *OutMessage) AppendString(src string) {
	p := b.GrowNoZero(uintptr(len(src)))
	if p == nil {
		panic(fmt.Sprintf("Can't grow %d bytes", len(src)))
	}

	sh := (*reflect.StringHeader)(unsafe.Pointer(&src))
	memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len))

	return
}

// Return the current size of the buffer.
func (b *OutMessage) Len() int {
	panic("TODO")
}

// Return a reference to the current contents of the buffer.
func (b *OutMessage) Bytes() []byte {
	panic("TODO")
}
<commit_msg>Finished OutMessage.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package buffer

import (
	"fmt"
	"log"
	"reflect"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
)

const outHeaderSize = unsafe.Sizeof(fusekernel.OutHeader{})

// We size out messages to be large enough to hold a header for the response
// plus the largest read that may come in.
const outMessageSize = outHeaderSize + MaxReadSize

// OutMessage provides a mechanism for constructing a single contiguous fuse
// message from multiple segments, where the first segment is always a
// fusekernel.OutHeader message.
//
// Must be initialized with Reset.
type OutMessage struct {
	offset  uintptr
	storage [outMessageSize]byte
}

// Make sure alignment works out correctly, at least for the header.
func init() {
	a := unsafe.Alignof(OutMessage{})
	o := unsafe.Offsetof(OutMessage{}.storage)
	e := unsafe.Alignof(fusekernel.OutHeader{})

	if a%e != 0 || o%e != 0 {
		log.Panicf("Bad alignment or offset: %d, %d, need %d", a, o, e)
	}
}

// Reset the message so that it is ready to be used again. Afterward, the
// contents are solely a zeroed header.
func (m *OutMessage) Reset() {
	m.offset = outHeaderSize
	memclr(unsafe.Pointer(&m.storage), outHeaderSize)
}

// Return a pointer to the header at the start of the message.
func (b *OutMessage) OutHeader() (h *fusekernel.OutHeader) {
	h = (*fusekernel.OutHeader)(unsafe.Pointer(&b.storage))
	return
}

// Grow the buffer by the supplied number of bytes, returning a pointer to the
// start of the new segment, which is zeroed. If there is no space left, return
// the nil pointer.
func (b *OutMessage) Grow(size uintptr) (p unsafe.Pointer) {
	p = b.GrowNoZero(size)
	if p != nil {
		memclr(p, size)
	}

	return
}

// Equivalent to Grow, except the new segment is not zeroed. Use with caution!
func (b *OutMessage) GrowNoZero(size uintptr) (p unsafe.Pointer) {
	if outMessageSize-b.offset < size {
		return
	}

	p = unsafe.Pointer(uintptr(unsafe.Pointer(&b.storage)) + b.offset)
	b.offset += size

	return
}

// Equivalent to growing by the length of p, then copying p over the new
// segment. Panics if there is not enough room available.
func (b *OutMessage) Append(src []byte) {
	p := b.GrowNoZero(uintptr(len(src)))
	if p == nil {
		panic(fmt.Sprintf("Can't grow %d bytes", len(src)))
	}

	sh := (*reflect.SliceHeader)(unsafe.Pointer(&src))
	memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len))

	return
}

// Equivalent to growing by the length of s, then copying s over the new
// segment. Panics if there is not enough room available.
func (b *OutMessage) AppendString(src string) {
	p := b.GrowNoZero(uintptr(len(src)))
	if p == nil {
		panic(fmt.Sprintf("Can't grow %d bytes", len(src)))
	}

	sh := (*reflect.StringHeader)(unsafe.Pointer(&src))
	memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len))

	return
}

// Return the current size of the buffer.
func (b *OutMessage) Len() int {
	return int(b.offset)
}

// Return a reference to the current contents of the buffer.
func (b *OutMessage) Bytes() []byte {
	return b.storage[:int(b.offset)]
}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"
	"os"
	"time"

	"github.com/nsec/askgod/api"
	"github.com/nsec/askgod/internal/utils"
)

// GetTeamPoints returns the current total for the team
func (db *DB) GetTeamPoints(teamid int64) (int64, error) {
	total := int64(0)

	// Get the total
	err := db.QueryRow("SELECT COALESCE(SUM(score.value), 0) AS points FROM score WHERE teamid=$1", teamid).Scan(&total)
	if err != nil {
		return -1, err
	}

	return total, nil
}

// GetTeamFlags retrieves all the score entries for the team
func (db *DB) GetTeamFlags(teamid int64) ([]api.Flag, error) {
	// Return a list of score entries
	resp := []api.Flag{}

	// Query all the scores from the database
	rows, err := db.Query("SELECT score.flagid, flag.description, score.value, score.notes, score.submit_time, flag.return_string FROM score LEFT JOIN flag ON flag.id=score.flagid WHERE score.teamid=$1 ORDER BY score.submit_time ASC;", teamid)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// Iterate through the results
	for rows.Next() {
		row := api.Flag{}

		err := rows.Scan(&row.ID, &row.Description, &row.Value, &row.Notes, &row.SubmitTime, &row.ReturnString)
		if err != nil {
			return nil, err
		}

		resp = append(resp, row)
	}

	// Check for any error that might have happened
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return resp, nil
}

// GetTeamFlag retrieves a single score entry for the team
func (db *DB) GetTeamFlag(teamid int64, id int64) (*api.Flag, error) {
	// Return a list of score entries
	resp := api.Flag{}

	// Query all the scores from the database
	err := db.QueryRow("SELECT score.flagid, flag.description, score.value, score.notes, score.submit_time, flag.return_string FROM score LEFT JOIN flag ON flag.id=score.flagid WHERE score.teamid=$1 AND score.flagid=$2 ORDER BY score.submit_time ASC;", teamid, id).Scan(
		&resp.ID, &resp.Description, &resp.Value, &resp.Notes, &resp.SubmitTime, &resp.ReturnString)
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// UpdateTeamFlag updates a single score entry for the team
func (db *DB) UpdateTeamFlag(teamid int64, id int64, flag api.FlagPut) error {
	// Update the database entry
	result, err := db.Exec("UPDATE score SET notes=$1 WHERE teamid=$2 AND flagid=$3;",
		flag.Notes, teamid, id)
	if err != nil {
		return err
	}

	// Check that a change indeed happened
	count, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if count == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// SubmitTeamFlag validates a submitted flag and adds it to the database
func (db *DB) SubmitTeamFlag(teamid int64, flag api.FlagPost) (*api.Flag, *api.AdminFlag, error) {
	// Query the database entry
	row := api.AdminFlag{}
	tags := ""
	err := db.QueryRow("SELECT id, flag, value, return_string, description, tags FROM flag WHERE flag=$1;", flag.Flag).Scan(
		&row.ID, &row.Flag, &row.Value, &row.ReturnString, &row.Description, &tags)
	if err != nil {
		return nil, nil, err
	}

	row.Tags, err = utils.ParseTags(tags)
	if err != nil {
		return nil, nil, err
	}

	// Check if already submitted
	id := int64(-1)
	err = db.QueryRow("SELECT id FROM score WHERE teamid=$1 AND flagid=$2;", teamid, row.ID).Scan(&id)
	if err == nil {
		return nil, &row, os.ErrExist
	} else if err != sql.ErrNoRows {
		return nil, nil, err
	}

	// Add the flag
	id = -1
	err = db.QueryRow("INSERT INTO score (teamid, flagid, value, notes, submit_time) VALUES ($1, $2, $3, $4, $5) RETURNING id;",
		teamid, row.ID, row.Value, flag.Notes, time.Now()).Scan(&id)
	if err != nil {
		return nil, nil, err
	}

	// Query the new entry
	result := api.Flag{}
	err = db.QueryRow("SELECT score.flagid, flag.description, score.value, score.notes, score.submit_time, flag.return_string FROM score LEFT JOIN flag ON flag.id=score.flagid WHERE score.id=$1;", id).Scan(
		&result.ID, &result.Description, &result.Value, &result.Notes, &result.SubmitTime, &result.ReturnString)
	if err != nil {
		return nil, nil, err
	}

	return &result, &row, nil
}

// GetScores retrieves all the score entries from the database
func (db *DB) GetScores() ([]api.AdminScore, error) {
	// Return a list of score entries
	resp := []api.AdminScore{}

	// Query all the scores from the database
	rows, err := db.Query("SELECT id, teamid, flagid, value, notes, submit_time FROM score ORDER BY id ASC;")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// Iterate through the results
	for rows.Next() {
		row := api.AdminScore{}

		err := rows.Scan(&row.ID, &row.TeamID, &row.FlagID, &row.Value, &row.Notes, &row.SubmitTime)
		if err != nil {
			return nil, err
		}

		resp = append(resp, row)
	}

	// Check for any error that might have happened
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return resp, nil
}

// GetScore retrieves a single score entry from the database
func (db *DB) GetScore(id int64) (*api.AdminScore, error) {
	// Query the database entry
	row := api.AdminScore{}
	err := db.QueryRow("SELECT id, teamid, flagid, value, notes, submit_time FROM score WHERE id=$1;", id).Scan(
		&row.ID, &row.TeamID, &row.FlagID, &row.Value, &row.Notes, &row.SubmitTime)
	if err != nil {
		return nil, err
	}

	return &row, nil
}

// CreateScore adds a new score entry to the database
func (db *DB) CreateScore(score api.AdminScorePost) (int64, error) {
	id := int64(-1)

	// Create the database entry
	err := db.QueryRow("INSERT INTO score (teamid, flagid, value, notes, submit_time) VALUES ($1, $2, $3, $4, $5) RETURNING id",
		score.TeamID, score.FlagID, score.Value, score.Notes, time.Now()).Scan(&id)
	if err != nil {
		return -1, err
	}

	return id, nil
}

// UpdateScore updates an existing score entry
func (db *DB) UpdateScore(id int64, score api.AdminScorePut) error {
	// Update the database entry
	result, err := db.Exec("UPDATE score SET value=$1, notes=$2 WHERE id=$3;",
		score.Value, score.Notes, id)
	if err != nil {
		return err
	}

	// Check that a change indeed happened
	count, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if count == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// DeleteScore deletes a single score entry from the database
func (db *DB) DeleteScore(id int64) error {
	// Delete the database entry
	result, err := db.Exec("DELETE FROM score WHERE id=$1;", id)
	if err != nil {
		return err
	}

	// Check that a change indeed happened
	count, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if count == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// ClearScores wipes all score entries from the database
func (db *DB) ClearScores() error {
	// Start a transaction
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Wipe the table
	_, err = tx.Exec("DELETE FROM score;")
	if err != nil {
		tx.Rollback()
		return err
	}

	// Reset the sequence
	_, err = tx.Exec("ALTER SEQUENCE score_id_seq RESTART;")
	if err != nil {
		tx.Rollback()
		return err
	}

	// Commit
	err = tx.Commit()
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>askgod-server: Flags are case insensitive<commit_after>package database

import (
	"database/sql"
	"os"
	"time"

	"github.com/nsec/askgod/api"
	"github.com/nsec/askgod/internal/utils"
)

// GetTeamPoints returns the current total for the team
func (db *DB) GetTeamPoints(teamid int64) (int64, error) {
	total := int64(0)

	// Get the total
	err := db.QueryRow("SELECT COALESCE(SUM(score.value), 0) AS points FROM score WHERE teamid=$1", teamid).Scan(&total)
	if err != nil {
		return -1, err
	}

	return total, nil
}

// GetTeamFlags retrieves all the score entries for the team
func (db *DB) GetTeamFlags(teamid int64) ([]api.Flag, error) {
	// Return a list of score entries
	resp := []api.Flag{}

	// Query all the scores from the database
	rows, err := db.Query("SELECT score.flagid, flag.description, score.value, score.notes, score.submit_time, flag.return_string FROM score LEFT JOIN flag ON flag.id=score.flagid WHERE score.teamid=$1 ORDER BY score.submit_time ASC;", teamid)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// Iterate through the results
	for rows.Next() {
		row := api.Flag{}

		err := rows.Scan(&row.ID, &row.Description, &row.Value, &row.Notes, &row.SubmitTime, &row.ReturnString)
		if err != nil {
			return nil, err
		}

		resp = append(resp, row)
	}

	// Check for any error that might have happened
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return resp, nil
}

// GetTeamFlag retrieves a single score entry for the team
func (db *DB) GetTeamFlag(teamid int64, id int64) (*api.Flag, error) {
	// Return a list of score entries
	resp := api.Flag{}

	// Query all the scores from the database
	err := db.QueryRow("SELECT score.flagid, flag.description, score.value, score.notes, score.submit_time, flag.return_string FROM score LEFT JOIN flag ON flag.id=score.flagid WHERE score.teamid=$1 AND score.flagid=$2 ORDER BY score.submit_time ASC;", teamid, id).Scan(
		&resp.ID, &resp.Description, &resp.Value, &resp.Notes, &resp.SubmitTime, &resp.ReturnString)
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

// UpdateTeamFlag updates a single score entry for the team
func (db *DB) UpdateTeamFlag(teamid int64, id int64, flag api.FlagPut) error {
	// Update the database entry
	result, err := db.Exec("UPDATE score SET notes=$1 WHERE teamid=$2 AND flagid=$3;",
		flag.Notes, teamid, id)
	if err != nil {
		return err
	}

	// Check that a change indeed happened
	count, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if count == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// SubmitTeamFlag validates a submitted flag and adds it to the database
func (db *DB) SubmitTeamFlag(teamid int64, flag api.FlagPost) (*api.Flag, *api.AdminFlag, error) {
	// Query the database entry
	row := api.AdminFlag{}
	tags := ""
	err := db.QueryRow("SELECT id, flag, value, return_string, description, tags FROM flag WHERE LOWER(flag)=LOWER($1);", flag.Flag).Scan(
		&row.ID, &row.Flag, &row.Value, &row.ReturnString, &row.Description, &tags)
	if err != nil {
		return nil, nil, err
	}

	row.Tags, err = utils.ParseTags(tags)
	if err != nil {
		return nil, nil, err
	}

	// Check if already submitted
	id := int64(-1)
	err = db.QueryRow("SELECT id FROM score WHERE teamid=$1 AND flagid=$2;", teamid, row.ID).Scan(&id)
	if err == nil {
		return nil, &row, os.ErrExist
	} else if err != sql.ErrNoRows {
		return nil, nil, err
	}

	// Add the flag
	id = -1
	err = db.QueryRow("INSERT INTO score (teamid, flagid, value, notes, submit_time) VALUES ($1, $2, $3, $4, $5) RETURNING id;",
		teamid, row.ID, row.Value, flag.Notes, time.Now()).Scan(&id)
	if err != nil {
		return nil, nil, err
	}

	// Query the new entry
	result := api.Flag{}
	err = db.QueryRow("SELECT score.flagid, flag.description, score.value, score.notes, score.submit_time, flag.return_string FROM score LEFT JOIN flag ON flag.id=score.flagid WHERE score.id=$1;", id).Scan(
		&result.ID, &result.Description, &result.Value, &result.Notes, &result.SubmitTime, &result.ReturnString)
	if err != nil {
		return nil, nil, err
	}

	return &result, &row, nil
}

// GetScores retrieves all the score entries from the database
func (db *DB) GetScores() ([]api.AdminScore, error) {
	// Return a list of score entries
	resp := []api.AdminScore{}

	// Query all the scores from the database
	rows, err := db.Query("SELECT id, teamid, flagid, value, notes, submit_time FROM score ORDER BY id ASC;")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// Iterate through the results
	for rows.Next() {
		row := api.AdminScore{}

		err := rows.Scan(&row.ID, &row.TeamID, &row.FlagID, &row.Value, &row.Notes, &row.SubmitTime)
		if err != nil {
			return nil, err
		}

		resp = append(resp, row)
	}

	// Check for any error that might have happened
	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return resp, nil
}

// GetScore retrieves a single score entry from the database
func (db *DB) GetScore(id int64) (*api.AdminScore, error) {
	// Query the database entry
	row := api.AdminScore{}
	err := db.QueryRow("SELECT id, teamid, flagid, value, notes, submit_time FROM score WHERE id=$1;", id).Scan(
		&row.ID, &row.TeamID, &row.FlagID, &row.Value, &row.Notes, &row.SubmitTime)
	if err != nil {
		return nil, err
	}

	return &row, nil
}

// CreateScore adds a new score entry to the database
func (db *DB) CreateScore(score api.AdminScorePost) (int64, error) {
	id := int64(-1)

	// Create the database entry
	err := db.QueryRow("INSERT INTO score (teamid, flagid, value, notes, submit_time) VALUES ($1, $2, $3, $4, $5) RETURNING id",
		score.TeamID, score.FlagID, score.Value, score.Notes, time.Now()).Scan(&id)
	if err != nil {
		return -1, err
	}

	return id, nil
}

// UpdateScore updates an existing score entry
func (db *DB) UpdateScore(id int64, score api.AdminScorePut) error {
	// Update the database entry
	result, err := db.Exec("UPDATE score SET value=$1, notes=$2 WHERE id=$3;",
		score.Value, score.Notes, id)
	if err != nil {
		return err
	}

	// Check that a change indeed happened
	count, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if count == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// DeleteScore deletes a single score entry from the database
func (db *DB) DeleteScore(id int64) error {
	// Delete the database entry
	result, err := db.Exec("DELETE FROM score WHERE id=$1;", id)
	if err != nil {
		return err
	}

	// Check that a change indeed happened
	count, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if count == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// ClearScores wipes all score entries from the database
func (db *DB) ClearScores() error {
	// Start a transaction
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Wipe the table
	_, err = tx.Exec("DELETE FROM score;")
	if err != nil {
		tx.Rollback()
		return err
	}

	// Reset the sequence
	_, err = tx.Exec("ALTER SEQUENCE score_id_seq RESTART;")
	if err != nil {
		tx.Rollback()
		return err
	}

	// Commit
	err = tx.Commit()
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package nim

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"strconv"
	"time"

	"github.com/astaxie/beego/httplib"
)

func getCheckSum(appSecret string, nonce string, curTime string) string {
	var buf bytes.Buffer
	buf.WriteString(appSecret)
	buf.WriteString(nonce)
	buf.WriteString(curTime)
	h := sha1.New()
	io.WriteString(h, buf.String())
	return fmt.Sprintf("%x", h.Sum(nil))
}

//SetNimReqHeader set nim appkey, nonce, curtime, checkSum
func SetNimReqHeader(req *httplib.BeegoHTTPRequest, appkey string, nonce string, appsecret string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	curTime := time.Now().UTC().Unix()
	checkSum := getCheckSum(appsecret, nonce, strconv.FormatInt(curTime, 10))
	req.Header("AppKey", appkey)
	req.Header("Nonce", nonce)
	req.Header("CurTime", strconv.FormatInt(curTime, 10))
	req.Header("CheckSum", checkSum)
	req.Header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
	return nil
}

//BuildSendMsgReq set param for send msg
func BuildSendMsgReq(req *httplib.BeegoHTTPRequest, from string, ope int, to string, msgtype int, body string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("from", from)
	req.Param("ope", strconv.Itoa(ope))
	req.Param("to", to)
	req.Param("type", strconv.Itoa(msgtype))
	req.Param("body", body)
	return nil
}

//BuildKickReq set param for kick
func BuildKickReq(req *httplib.BeegoHTTPRequest, tid string, owner string, member string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("member", member)
	return nil
}

//BuildRemoveReq set param for remove
func BuildRemoveReq(req *httplib.BeegoHTTPRequest, tid string, owner string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	return nil
}

//BuildChangeOwnerReq set param for changeOwner
func BuildChangeOwnerReq(req *httplib.BeegoHTTPRequest, tid string, owner string, newowner string, leave int) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("newowner", newowner)
	req.Param("leave", strconv.Itoa(leave))
	return nil
}

//BuildAddManagerReq set param for addManager
func BuildAddManagerReq(req *httplib.BeegoHTTPRequest, tid string, owner string, members string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("members", members)
	return nil
}

//BuildRemoveManagerReq set param for removeManager
func BuildRemoveManagerReq(req *httplib.BeegoHTTPRequest, tid string, owner string, members string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("members", members)
	return nil
}
<commit_msg>refactor(nim):correct change team owner function name<commit_after>package nim

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"strconv"
	"time"

	"github.com/astaxie/beego/httplib"
)

func getCheckSum(appSecret string, nonce string, curTime string) string {
	var buf bytes.Buffer
	buf.WriteString(appSecret)
	buf.WriteString(nonce)
	buf.WriteString(curTime)
	h := sha1.New()
	io.WriteString(h, buf.String())
	return fmt.Sprintf("%x", h.Sum(nil))
}

//SetNimReqHeader set nim appkey, nonce, curtime, checkSum
func SetNimReqHeader(req *httplib.BeegoHTTPRequest, appkey string, nonce string, appsecret string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	curTime := time.Now().UTC().Unix()
	checkSum := getCheckSum(appsecret, nonce, strconv.FormatInt(curTime, 10))
	req.Header("AppKey", appkey)
	req.Header("Nonce", nonce)
	req.Header("CurTime", strconv.FormatInt(curTime, 10))
	req.Header("CheckSum", checkSum)
	req.Header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
	return nil
}

//BuildSendMsgReq set param for send msg
func BuildSendMsgReq(req *httplib.BeegoHTTPRequest, from string, ope int, to string, msgtype int, body string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("from", from)
	req.Param("ope", strconv.Itoa(ope))
	req.Param("to", to)
	req.Param("type", strconv.Itoa(msgtype))
	req.Param("body", body)
	return nil
}

//BuildKickReq set param for kick
func BuildKickReq(req *httplib.BeegoHTTPRequest, tid string, owner string, member string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("member", member)
	return nil
}

//BuildRemoveReq set param for remove
func BuildRemoveReq(req *httplib.BeegoHTTPRequest, tid string, owner string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	return nil
}

//BuildChangeTeamOwnerReq set param for changeOwner
func BuildChangeTeamOwnerReq(req *httplib.BeegoHTTPRequest, tid string, owner string, newowner string, leave int) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("newowner", newowner)
	req.Param("leave", strconv.Itoa(leave))
	return nil
}

//BuildAddManagerReq set param for addManager
func BuildAddManagerReq(req *httplib.BeegoHTTPRequest, tid string, owner string, members string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("members", members)
	return nil
}

//BuildRemoveManagerReq set param for removeManager
func BuildRemoveManagerReq(req *httplib.BeegoHTTPRequest, tid string, owner string, members string) error {
	if req == nil {
		return errors.New("req is nil")
	}
	req.Param("tid", tid)
	req.Param("owner", owner)
	req.Param("members", members)
	return nil
}
<|endoftext|>