<commit_before>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ltm

import "github.com/e-XpertSolutions/f5-rest-client/f5"

// A PoolList holds a list of Pool.
type PoolList struct {
	Items    []Pool `json:"items,omitempty"`
	Kind     string `json:"kind,omitempty" pretty:",expanded"`
	SelfLink string `json:"selfLink,omitempty" pretty:",expanded"`
}

// A Pool hold the uration for a pool.
type Pool struct {
	AllowNat              string   `json:"allowNat,omitempty" pretty:",expanded"`
	AllowSnat             string   `json:"allowSnat,omitempty" pretty:",expanded"`
	FullPath              string   `json:"fullPath,omitempty" pretty:",expanded"`
	Generation            int64    `json:"generation,omitempty" pretty:",expanded"`
	IgnorePersistedWeight string   `json:"ignorePersistedWeight,omitempty" pretty:",expanded"`
	IPTosToClient         string   `json:"ipTosToClient,omitempty" pretty:",expanded"`
	IPTosToServer         string   `json:"ipTosToServer,omitempty" pretty:",expanded"`
	Kind                  string   `json:"kind,omitempty" pretty:",expanded"`
	LinkQosToClient       string   `json:"linkQosToClient,omitempty" pretty:",expanded"`
	LinkQosToServer       string   `json:"linkQosToServer,omitempty" pretty:",expanded"`
	LoadBalancingMode     string   `json:"loadBalancingMode,omitempty"`
	Members               []string `json:"items,omitempty"`
	MembersReference      struct {
		IsSubcollection bool          `json:"isSubcollection,omitempty"`
		Link            string        `json:"link,omitempty"`
		Members         []PoolMembers `json:"items,omitempty"`
	} `json:"membersReference,omitempty"`
	MinActiveMembers       int64  `json:"minActiveMembers,omitempty"`
	MinUpMembers           int64  `json:"minUpMembers,omitempty"`
	MinUpMembersAction     string `json:"minUpMembersAction,omitempty"`
	MinUpMembersChecking   string `json:"minUpMembersChecking,omitempty"`
	Monitor                string `json:"monitor,omitempty"`
	Name                   string `json:"name,omitempty"`
	QueueDepthLimit        int64  `json:"queueDepthLimit,omitempty" pretty:",expanded"`
	QueueOnConnectionLimit string `json:"queueOnConnectionLimit,omitempty" pretty:",expanded"`
	QueueTimeLimit         int64  `json:"queueTimeLimit,omitempty" pretty:",expanded"`
	ReselectTries          int64  `json:"reselectTries,omitempty"`
	SelfLink               string `json:"selfLink,omitempty" pretty:",expanded"`
	ServiceDownAction      string `json:"serviceDownAction,omitempty"`
	SlowRampTime           int64  `json:"slowRampTime,omitempty" pretty:",expanded"`
	Partition              int64  `json:"partition,omitempty"`
}

// PoolEndpoint represents the REST resource for managing a pool.
const PoolEndpoint = "/pool"

// A PoolResource provides API to manage pool uration.
type PoolResource struct {
	c *f5.Client
}

// ListAll lists all the pool urations.
func (pr *PoolResource) ListAll() (*PoolList, error) {
	var list PoolList
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// Get a single pool uration identified by id.
func (pr *PoolResource) Get(id string) (*Pool, error) {
	var item Pool
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint+"/"+id, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// Create a new pool uration.
func (pr *PoolResource) Create(item Pool) error {
	if err := pr.c.ModQuery("POST", BasePath+PoolEndpoint, item); err != nil {
		return err
	}
	return nil
}

// Edit a pool uration identified by id.
func (pr *PoolResource) Edit(id string, item Pool) error {
	if err := pr.c.ModQuery("PUT", BasePath+PoolEndpoint+"/"+id, item); err != nil {
		return err
	}
	return nil
}

// Delete a single pool uration identified by id.
func (pr *PoolResource) Delete(id string) error {
	if err := pr.c.ModQuery("DELETE", BasePath+PoolEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}

// GetMembers gets all the members associated to the pool identified by id.
func (pr *PoolResource) GetMembers(id string) (*PoolMembersList, error) {
	var poolMembers PoolMembersList
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint+"/"+id+"/members", &poolMembers); err != nil {
		return nil, err
	}
	return &poolMembers, nil
}

func (pr *PoolResource) AddMember(id string, poolMember PoolMembers) error {
	if err := pr.c.ModQuery("POST", BasePath+PoolEndpoint+"/"+id+"/members", poolMember); err != nil {
		return err
	}
	return nil
}

func (pr *PoolResource) ShowStats(id string) (*PoolStatsList, error) {
	var item PoolStatsList
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint+"/"+id+"/stats", &item); err != nil {
		return nil, err
	}
	return &item, nil
}
<commit_msg>Change pool partition type to string. Fix https://github.com/e-XpertSolutions/f5-rest-client/issues/15<commit_after>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ltm

import "github.com/e-XpertSolutions/f5-rest-client/f5"

// A PoolList holds a list of Pool.
type PoolList struct {
	Items    []Pool `json:"items,omitempty"`
	Kind     string `json:"kind,omitempty" pretty:",expanded"`
	SelfLink string `json:"selfLink,omitempty" pretty:",expanded"`
}

// A Pool hold the uration for a pool.
type Pool struct {
	AllowNat              string   `json:"allowNat,omitempty" pretty:",expanded"`
	AllowSnat             string   `json:"allowSnat,omitempty" pretty:",expanded"`
	FullPath              string   `json:"fullPath,omitempty" pretty:",expanded"`
	Generation            int64    `json:"generation,omitempty" pretty:",expanded"`
	IgnorePersistedWeight string   `json:"ignorePersistedWeight,omitempty" pretty:",expanded"`
	IPTosToClient         string   `json:"ipTosToClient,omitempty" pretty:",expanded"`
	IPTosToServer         string   `json:"ipTosToServer,omitempty" pretty:",expanded"`
	Kind                  string   `json:"kind,omitempty" pretty:",expanded"`
	LinkQosToClient       string   `json:"linkQosToClient,omitempty" pretty:",expanded"`
	LinkQosToServer       string   `json:"linkQosToServer,omitempty" pretty:",expanded"`
	LoadBalancingMode     string   `json:"loadBalancingMode,omitempty"`
	Members               []string `json:"items,omitempty"`
	MembersReference      struct {
		IsSubcollection bool          `json:"isSubcollection,omitempty"`
		Link            string        `json:"link,omitempty"`
		Members         []PoolMembers `json:"items,omitempty"`
	} `json:"membersReference,omitempty"`
	MinActiveMembers       int64  `json:"minActiveMembers,omitempty"`
	MinUpMembers           int64  `json:"minUpMembers,omitempty"`
	MinUpMembersAction     string `json:"minUpMembersAction,omitempty"`
	MinUpMembersChecking   string `json:"minUpMembersChecking,omitempty"`
	Monitor                string `json:"monitor,omitempty"`
	Name                   string `json:"name,omitempty"`
	QueueDepthLimit        int64  `json:"queueDepthLimit,omitempty" pretty:",expanded"`
	QueueOnConnectionLimit string `json:"queueOnConnectionLimit,omitempty" pretty:",expanded"`
	QueueTimeLimit         int64  `json:"queueTimeLimit,omitempty" pretty:",expanded"`
	ReselectTries          int64  `json:"reselectTries,omitempty"`
	SelfLink               string `json:"selfLink,omitempty" pretty:",expanded"`
	ServiceDownAction      string `json:"serviceDownAction,omitempty"`
	SlowRampTime           int64  `json:"slowRampTime,omitempty" pretty:",expanded"`
	Partition              string `json:"partition,omitempty"`
}

// PoolEndpoint represents the REST resource for managing a pool.
const PoolEndpoint = "/pool"

// A PoolResource provides API to manage pool uration.
type PoolResource struct {
	c *f5.Client
}

// ListAll lists all the pool urations.
func (pr *PoolResource) ListAll() (*PoolList, error) {
	var list PoolList
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// Get a single pool uration identified by id.
func (pr *PoolResource) Get(id string) (*Pool, error) {
	var item Pool
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint+"/"+id, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// Create a new pool uration.
func (pr *PoolResource) Create(item Pool) error {
	if err := pr.c.ModQuery("POST", BasePath+PoolEndpoint, item); err != nil {
		return err
	}
	return nil
}

// Edit a pool uration identified by id.
func (pr *PoolResource) Edit(id string, item Pool) error {
	if err := pr.c.ModQuery("PUT", BasePath+PoolEndpoint+"/"+id, item); err != nil {
		return err
	}
	return nil
}

// Delete a single pool uration identified by id.
func (pr *PoolResource) Delete(id string) error {
	if err := pr.c.ModQuery("DELETE", BasePath+PoolEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}

// GetMembers gets all the members associated to the pool identified by id.
func (pr *PoolResource) GetMembers(id string) (*PoolMembersList, error) {
	var poolMembers PoolMembersList
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint+"/"+id+"/members", &poolMembers); err != nil {
		return nil, err
	}
	return &poolMembers, nil
}

func (pr *PoolResource) AddMember(id string, poolMember PoolMembers) error {
	if err := pr.c.ModQuery("POST", BasePath+PoolEndpoint+"/"+id+"/members", poolMember); err != nil {
		return err
	}
	return nil
}

func (pr *PoolResource) ShowStats(id string) (*PoolStatsList, error) {
	var item PoolStatsList
	if err := pr.c.ReadQuery(BasePath+PoolEndpoint+"/"+id+"/stats", &item); err != nil {
		return nil, err
	}
	return &item, nil
}
<|endoftext|>


<commit_before>// 7 july 2014

package ui

// Window represents a top-level window on screen that contains other Controls.
// Windows in package ui can only contain one control; the Stack and Grid layout Controls allow you to pack multiple Controls in a Window.
// Note that a Window is not itself a Control.
type Window interface {
	// SetControl sets the Window's child Control.
	SetControl(c Control)

	// Title and SetTitle get and set the Window's title, respectively.
	Title() string
	SetTitle(title string)

	// Show and Hide bring the Window on-screen and off-screen, respectively.
	Show()
	Hide()

	// Close closes the Window.
	// Any Controls within the Window are destroyed, and the Window itself is also destroyed.
	// Attempting to use a Window after it has been closed results in undefined behavior.
	// Close unconditionally closes the Window; it neither raises OnClosing nor checks for a return from OnClosing.
	// TODO make sure the above happens on GTK+ and Mac OS X; it does on Windows
	Close()

	// OnClosing registers an event handler that is triggered when the user clicks the Window's close button.
	// On systems where whole applications own windows, OnClosing is also triggered when the user asks to close the application.
	// If this handler returns true, the Window is closed as defined by Close above.
	// If this handler returns false, the Window is not closed.
	OnClosing(func() bool)
}

// NewWindow creates a new Window with the given title text and size.
func NewWindow(title string, width int, height int) Window {
	return newWindow(title, width, height)
}
<commit_msg>Verified that Window.Close() works as documented on GTK+.<commit_after>// 7 july 2014

package ui

// Window represents a top-level window on screen that contains other Controls.
// Windows in package ui can only contain one control; the Stack and Grid layout Controls allow you to pack multiple Controls in a Window.
// Note that a Window is not itself a Control.
type Window interface {
	// SetControl sets the Window's child Control.
	SetControl(c Control)

	// Title and SetTitle get and set the Window's title, respectively.
	Title() string
	SetTitle(title string)

	// Show and Hide bring the Window on-screen and off-screen, respectively.
	Show()
	Hide()

	// Close closes the Window.
	// Any Controls within the Window are destroyed, and the Window itself is also destroyed.
	// Attempting to use a Window after it has been closed results in undefined behavior.
	// Close unconditionally closes the Window; it neither raises OnClosing nor checks for a return from OnClosing.
	// TODO make sure the above happens on Mac OS X; it does on Windows and GTK+
	Close()

	// OnClosing registers an event handler that is triggered when the user clicks the Window's close button.
	// On systems where whole applications own windows, OnClosing is also triggered when the user asks to close the application.
	// If this handler returns true, the Window is closed as defined by Close above.
	// If this handler returns false, the Window is not closed.
	OnClosing(func() bool)
}

// NewWindow creates a new Window with the given title text and size.
func NewWindow(title string, width int, height int) Window {
	return newWindow(title, width, height)
}
<|endoftext|>


<commit_before>package main

// This utility moves a network interface into a network namespace and
// configures it. The configuration is passed in as a JSON object
// (marshalled prot.NetworkAdapter).  It is necessary to implement
// this as a separate utility as in Go one does not have tight control
// over which OS thread a given Go thread/routing executes but as can
// only enter a namespace with a specific OS thread.
//
// Note, this logs to stdout so that the caller (gcs) can log the
// output itself.

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/Microsoft/opengcs/service/gcs/prot"
	"github.com/Microsoft/opengcs/service/gcsutils/gcstools/commoncli"
	log "github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

func netnsConfigMain() {
	if err := netnsConfig(); err != nil {
		log.Errorf("netnsConfig returned: %s", err)
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(-1)
	}
	log.Info("netnsConfig succeeded")
	os.Exit(0)
}

func netnsConfig() error {
	ifStr := flag.String("if", "", "Interface/Adapter to move/configure")
	nspid := flag.Int("nspid", -1, "Process ID (to locate netns")
	cfgStr := flag.String("cfg", "", "Adapter configuration (json)")
	logArgs := commoncli.SetFlagsForLogging()

	flag.Parse()
	if err := commoncli.SetupLogging(logArgs...); err != nil {
		return err
	}
	if *ifStr == "" || *nspid == -1 || *cfgStr == "" {
		return fmt.Errorf("All three arguments must be specified")
	}

	var a prot.NetworkAdapter
	if err := json.Unmarshal([]byte(*cfgStr), &a); err != nil {
		return err
	}

	if a.NatEnabled {
		log.Infof("Configure %s in %d with: %s/%d gw=%s", *ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
	} else {
		log.Infof("Configure %s in %d with DHCP", *ifStr, *nspid)
	}

	// Lock the OS Thread so we don't accidentally switch namespaces
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// Stash current network namespace away and make sure we enter it as we leave
	log.Infof("Obtaining current namespace")
	origNS, err := netns.Get()
	if err != nil {
		return fmt.Errorf("netns.Get() failed: %v", err)
	}
	defer origNS.Close()
	log.Infof("Original namespace %v", origNS)

	// Get a reference to the new network namespace
	ns, err := netns.GetFromPid(*nspid)
	if err != nil {
		return fmt.Errorf("netns.GetFromPid(%d) failed: %v", *nspid, err)
	}
	defer ns.Close()
	log.Infof("New network namespace from PID %d is %v", *nspid, ns)

	// Get a reference to the interface and make sure it's down
	link, err := netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}
	if err := netlink.LinkSetDown(link); err != nil {
		return fmt.Errorf("netlink.LinkSetDown(%#v) failed: %v", link, err)
	}

	// Move the interface to the new network namespace
	if err := netlink.LinkSetNsPid(link, *nspid); err != nil {
		return fmt.Errorf("netlink.SetNsPid(%#v, %d) failed: %v", link, *nspid, err)
	}

	log.Infof("Switching from %v to %v", origNS, ns)

	// Enter the new network namespace
	if err := netns.Set(ns); err != nil {
		return fmt.Errorf("netns.Set() failed: %v", err)
	}

	// Re-Get a reference to the interface (it may be a different ID in the new namespace)
	log.Infof("Getting reference to interface")
	link, err = netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}

	// User requested non-default MTU size
	if a.EncapOverhead != 0 {
		log.Info("EncapOverhead non-zero, will set MTU")
		mtu := link.Attrs().MTU - int(a.EncapOverhead)
		log.Infof("mtu %d", mtu)
		if err = netlink.LinkSetMTU(link, mtu); err != nil {
			return fmt.Errorf("netlink.LinkSetMTU(%#v, %d) failed: %v", link, mtu, err)
		}
	}

	// Configure the interface
	if a.NatEnabled {
		log.Info("Nat enabled - configuring interface")
		metric := 1
		if a.EnableLowMetric {
			metric = 500
		}

		// Bring the interface up
		if err := netlink.LinkSetUp(link); err != nil {
			return fmt.Errorf("netlink.LinkSetUp(%#v) failed: %v", link, err)
		}
		// Set IP address
		addr := &net.IPNet{
			IP: net.ParseIP(a.AllocatedIPAddress),
			// TODO(rn): This assumes/hardcodes IPv4
			Mask: net.CIDRMask(int(a.HostIPPrefixLength), 32)}
		ipAddr := &netlink.Addr{IPNet: addr, Label: ""}
		if err := netlink.AddrAdd(link, ipAddr); err != nil {
			return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr, err)
		}
		// Set gateway
		if a.HostIPAddress != "" {
			gw := net.ParseIP(a.HostIPAddress)

			if !addr.Contains(gw) {
				// In the case that a gw is not part of the subnet we are setting gw for,
				// a new addr containing this gw address need to be added into the link to avoid getting
				// unreachable error when adding this out-of-subnet gw route
				log.Infof("gw is outside of the subnet: Configure %s in %d with: %s/%d gw=%s\n",
					*ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
				addr2 := &net.IPNet{
					IP:   net.ParseIP(a.HostIPAddress),
					Mask: net.CIDRMask(32, 32)} // This assumes/hardcodes IPv4
				ipAddr2 := &netlink.Addr{IPNet: addr2, Label: ""}
				if err := netlink.AddrAdd(link, ipAddr2); err != nil {
					return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr2, err)
				}
			}
			route := netlink.Route{
				Scope:     netlink.SCOPE_UNIVERSE,
				LinkIndex: link.Attrs().Index,
				Gw:        gw,
				Priority:  metric, // This is what ip route add does
			}
			if err := netlink.RouteAdd(&route); err != nil {
				return fmt.Errorf("netlink.RouteAdd(%#v) failed: %v", route, err)
			}
		}
	} else {
		log.Infof("Execing udhcpc with timeout...")
		cmd := exec.Command("udhcpc", "-q", "-i", *ifStr, "-s", "/sbin/udhcpc_config.script")

		done := make(chan error)
		go func() {
			done <- cmd.Wait()
		}()
		defer close(done)

		select {
		case <-time.After(time.Duration(30 * time.Second)):
			var cos string
			co, err := cmd.CombinedOutput() // In case it has written something
			if err != nil {
				cos = string(co[:])
			}
			cmd.Process.Kill()
			log.Infof("udhcpc timed out [%s]", cos)
			return fmt.Errorf("udhcpc timed out. Failed to get DHCP address: %s", cos)
		case err := <-done:
			var cos string
			co, err := cmd.CombinedOutput() // Something should be on stderr
			if err != nil {
				cos = string(co[:])
			}
			if err != nil {
				log.Infof("udhcpc failed %s [%s]", err, cos)
				return fmt.Errorf("process failed: %s (%s)", err, cos)
			}
		}
		var cos string
		co, err := cmd.CombinedOutput()
		if err != nil {
			cos = string(co[:])
		}
		log.Debugf("udhcpc succeeded: %s", cos)
	}

	// Add some debug logging
	curNS, _ := netns.Get()
	// Refresh link attributes/state
	link, _ = netlink.LinkByIndex(link.Attrs().Index)
	attr := link.Attrs()
	addrs, _ := netlink.AddrList(link, 0)
	log.Infof("%v: %s[idx=%d,type=%s] is %v", curNS, attr.Name, attr.Index, link.Type(), attr.OperState)
	for _, addr := range addrs {
		log.Infof("  %v", addr)
	}

	return nil
}
<commit_msg>Ooops. Why we need CI back working<commit_after>package main

// This utility moves a network interface into a network namespace and
// configures it. The configuration is passed in as a JSON object
// (marshalled prot.NetworkAdapter).  It is necessary to implement
// this as a separate utility as in Go one does not have tight control
// over which OS thread a given Go thread/routing executes but as can
// only enter a namespace with a specific OS thread.
//
// Note, this logs to stdout so that the caller (gcs) can log the
// output itself.

import (
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/Microsoft/opengcs/service/gcs/prot"
	"github.com/Microsoft/opengcs/service/gcsutils/gcstools/commoncli"
	log "github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

func netnsConfigMain() {
	if err := netnsConfig(); err != nil {
		log.Errorf("netnsConfig returned: %s", err)
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(-1)
	}
	log.Info("netnsConfig succeeded")
	os.Exit(0)
}

func netnsConfig() error {
	ifStr := flag.String("if", "", "Interface/Adapter to move/configure")
	nspid := flag.Int("nspid", -1, "Process ID (to locate netns")
	cfgStr := flag.String("cfg", "", "Adapter configuration (json)")
	logArgs := commoncli.SetFlagsForLogging()

	flag.Parse()
	if err := commoncli.SetupLogging(logArgs...); err != nil {
		return err
	}
	if *ifStr == "" || *nspid == -1 || *cfgStr == "" {
		return fmt.Errorf("All three arguments must be specified")
	}

	var a prot.NetworkAdapter
	if err := json.Unmarshal([]byte(*cfgStr), &a); err != nil {
		return err
	}

	if a.NatEnabled {
		log.Infof("Configure %s in %d with: %s/%d gw=%s", *ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
	} else {
		log.Infof("Configure %s in %d with DHCP", *ifStr, *nspid)
	}

	// Lock the OS Thread so we don't accidentally switch namespaces
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// Stash current network namespace away and make sure we enter it as we leave
	log.Infof("Obtaining current namespace")
	origNS, err := netns.Get()
	if err != nil {
		return fmt.Errorf("netns.Get() failed: %v", err)
	}
	defer origNS.Close()
	log.Infof("Original namespace %v", origNS)

	// Get a reference to the new network namespace
	ns, err := netns.GetFromPid(*nspid)
	if err != nil {
		return fmt.Errorf("netns.GetFromPid(%d) failed: %v", *nspid, err)
	}
	defer ns.Close()
	log.Infof("New network namespace from PID %d is %v", *nspid, ns)

	// Get a reference to the interface and make sure it's down
	link, err := netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}
	if err := netlink.LinkSetDown(link); err != nil {
		return fmt.Errorf("netlink.LinkSetDown(%#v) failed: %v", link, err)
	}

	// Move the interface to the new network namespace
	if err := netlink.LinkSetNsPid(link, *nspid); err != nil {
		return fmt.Errorf("netlink.SetNsPid(%#v, %d) failed: %v", link, *nspid, err)
	}

	log.Infof("Switching from %v to %v", origNS, ns)

	// Enter the new network namespace
	if err := netns.Set(ns); err != nil {
		return fmt.Errorf("netns.Set() failed: %v", err)
	}

	// Re-Get a reference to the interface (it may be a different ID in the new namespace)
	log.Infof("Getting reference to interface")
	link, err = netlink.LinkByName(*ifStr)
	if err != nil {
		return fmt.Errorf("netlink.LinkByName(%s) failed: %v", *ifStr, err)
	}

	// User requested non-default MTU size
	if a.EncapOverhead != 0 {
		log.Info("EncapOverhead non-zero, will set MTU")
		mtu := link.Attrs().MTU - int(a.EncapOverhead)
		log.Infof("mtu %d", mtu)
		if err = netlink.LinkSetMTU(link, mtu); err != nil {
			return fmt.Errorf("netlink.LinkSetMTU(%#v, %d) failed: %v", link, mtu, err)
		}
	}

	// Configure the interface
	if a.NatEnabled {
		log.Info("Nat enabled - configuring interface")
		metric := 1
		if a.EnableLowMetric {
			metric = 500
		}

		// Bring the interface up
		if err := netlink.LinkSetUp(link); err != nil {
			return fmt.Errorf("netlink.LinkSetUp(%#v) failed: %v", link, err)
		}
		// Set IP address
		addr := &net.IPNet{
			IP: net.ParseIP(a.AllocatedIPAddress),
			// TODO(rn): This assumes/hardcodes IPv4
			Mask: net.CIDRMask(int(a.HostIPPrefixLength), 32)}
		ipAddr := &netlink.Addr{IPNet: addr, Label: ""}
		if err := netlink.AddrAdd(link, ipAddr); err != nil {
			return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr, err)
		}
		// Set gateway
		if a.HostIPAddress != "" {
			gw := net.ParseIP(a.HostIPAddress)

			if !addr.Contains(gw) {
				// In the case that a gw is not part of the subnet we are setting gw for,
				// a new addr containing this gw address need to be added into the link to avoid getting
				// unreachable error when adding this out-of-subnet gw route
				log.Infof("gw is outside of the subnet: Configure %s in %d with: %s/%d gw=%s\n",
					*ifStr, *nspid, a.AllocatedIPAddress, a.HostIPPrefixLength, a.HostIPAddress)
				addr2 := &net.IPNet{
					IP:   net.ParseIP(a.HostIPAddress),
					Mask: net.CIDRMask(32, 32)} // This assumes/hardcodes IPv4
				ipAddr2 := &netlink.Addr{IPNet: addr2, Label: ""}
				if err := netlink.AddrAdd(link, ipAddr2); err != nil {
					return fmt.Errorf("netlink.AddrAdd(%#v, %#v) failed: %v", link, ipAddr2, err)
				}
			}
			route := netlink.Route{
				Scope:     netlink.SCOPE_UNIVERSE,
				LinkIndex: link.Attrs().Index,
				Gw:        gw,
				Priority:  metric, // This is what ip route add does
			}
			if err := netlink.RouteAdd(&route); err != nil {
				return fmt.Errorf("netlink.RouteAdd(%#v) failed: %v", route, err)
			}
		}
	} else {
		log.Infof("Execing udhcpc with timeout...")
		cmd := exec.Command("udhcpc", "-q", "-i", *ifStr, "-s", "/sbin/udhcpc_config.script")

		done := make(chan error)
		go func() {
			done <- cmd.Wait()
		}()
		defer close(done)

		select {
		case <-time.After(time.Duration(30 * time.Second)):
			var cos string
			co, err := cmd.CombinedOutput() // In case it has written something
			if err != nil {
				cos = string(co[:])
			}
			cmd.Process.Kill()
			log.Infof("udhcpc timed out [%s]", cos)
			return fmt.Errorf("udhcpc timed out. Failed to get DHCP address: %s", cos)
		case err := <-done:
			var cos string
			co, err := cmd.CombinedOutput() // Something should be on stderr
			if err != nil {
				cos = string(co[:])
			}
			if err != nil {
				log.Infof("udhcpc failed %s [%s]", err, cos)
				return fmt.Errorf("process failed: %s (%s)", err, cos)
			}
		}
		var cos string
		co, err := cmd.CombinedOutput()
		if err != nil {
			cos = string(co[:])
		}
		log.Debugf("udhcpc succeeded: %s", cos)
	}

	// Add some debug logging
	curNS, _ := netns.Get()
	// Refresh link attributes/state
	link, _ = netlink.LinkByIndex(link.Attrs().Index)
	attr := link.Attrs()
	addrs, _ := netlink.AddrList(link, 0)
	log.Infof("%v: %s[idx=%d,type=%s] is %v", curNS, attr.Name, attr.Index, link.Type(), attr.OperState)
	for _, addr := range addrs {
		log.Infof("  %v", addr)
	}

	return nil
}
<|endoftext|>


<commit_before>// +build !windows

/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package proc

import (
	"context"
	"fmt"
	"io"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"sync/atomic"
	"syscall"

	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/namespaces"
	"github.com/containerd/containerd/runtime/proc"
	"github.com/containerd/fifo"
	runc "github.com/containerd/go-runc"
	"github.com/pkg/errors"
)

var bufPool = sync.Pool{
	New: func() interface{} {
		buffer := make([]byte, 32<<10)
		return &buffer
	},
}

type processIO struct {
	io runc.IO

	uri   *url.URL
	copy  bool
	stdio proc.Stdio
}

func (p *processIO) Close() error {
	if p.io != nil {
		return p.io.Close()
	}
	return nil
}

func (p *processIO) IO() runc.IO {
	return p.io
}

func (p *processIO) Copy(ctx context.Context, wg *sync.WaitGroup) error {
	if !p.copy {
		return nil
	}
	var cwg sync.WaitGroup
	if err := copyPipes(ctx, p.IO(), p.stdio.Stdin, p.stdio.Stdout, p.stdio.Stderr, wg, &cwg); err != nil {
		return errors.Wrap(err, "unable to copy pipes")
	}
	cwg.Wait()
	return nil
}

func createIO(ctx context.Context, id string, ioUID, ioGID int, stdio proc.Stdio) (*processIO, error) {
	pio := &processIO{
		stdio: stdio,
	}
	if stdio.IsNull() {
		i, err := runc.NewNullIO()
		if err != nil {
			return nil, err
		}
		pio.io = i
		return pio, nil
	}
	u, err := url.Parse(stdio.Stdout)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse stdout uri")
	}
	if u.Scheme == "" {
		u.Scheme = "fifo"
	}
	pio.uri = u
	switch u.Scheme {
	case "fifo":
		pio.copy = true
		pio.io, err = runc.NewPipeIO(ioUID, ioGID, withConditionalIO(stdio))
	case "binary":
		pio.io, err = newBinaryIO(ctx, id, u)
	case "file":
		if err := os.MkdirAll(filepath.Dir(u.Host), 0755); err != nil {
			return nil, err
		}
		var f *os.File
		f, err = os.OpenFile(u.Host, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			return nil, err
		}
		f.Close()
		pio.copy = true
		pio.io, err = runc.NewPipeIO(ioUID, ioGID, withConditionalIO(stdio))
	default:
		return nil, errors.Errorf("unknown STDIO scheme %s", u.Scheme)
	}
	if err != nil {
		return nil, err
	}
	return pio, nil
}

func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error {
	var sameFile *countingWriteCloser
	for _, i := range []struct {
		name string
		dest func(wc io.WriteCloser, rc io.Closer)
	}{
		{
			name: stdout,
			dest: func(wc io.WriteCloser, rc io.Closer) {
				wg.Add(1)
				cwg.Add(1)
				go func() {
					cwg.Done()
					p := bufPool.Get().(*[]byte)
					defer bufPool.Put(p)
					if _, err := io.CopyBuffer(wc, rio.Stdout(), *p); err != nil {
						log.G(ctx).Warn("error copying stdout")
					}
					wg.Done()
					wc.Close()
					if rc != nil {
						rc.Close()
					}
				}()
			},
		}, {
			name: stderr,
			dest: func(wc io.WriteCloser, rc io.Closer) {
				wg.Add(1)
				cwg.Add(1)
				go func() {
					cwg.Done()
					p := bufPool.Get().(*[]byte)
					defer bufPool.Put(p)
					if _, err := io.CopyBuffer(wc, rio.Stderr(), *p); err != nil {
						log.G(ctx).Warn("error copying stderr")
					}
					wg.Done()
					wc.Close()
					if rc != nil {
						rc.Close()
					}
				}()
			},
		},
	} {
		ok, err := isFifo(i.name)
		if err != nil {
			return err
		}
		var (
			fw io.WriteCloser
			fr io.Closer
		)
		if ok {
			if fw, err = fifo.OpenFifo(ctx, i.name, syscall.O_WRONLY, 0); err != nil {
				return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
			}
			if fr, err = fifo.OpenFifo(ctx, i.name, syscall.O_RDONLY, 0); err != nil {
				return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
			}
		} else {
			if sameFile != nil {
				sameFile.count++
				i.dest(sameFile, nil)
				continue
			}
			if fw, err = os.OpenFile(i.name, syscall.O_WRONLY|syscall.O_APPEND, 0); err != nil {
				return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
			}
			if stdout == stderr {
				sameFile = &countingWriteCloser{
					WriteCloser: fw,
					count:       1,
				}
			}
		}
		i.dest(fw, fr)
	}
	if stdin == "" {
		return nil
	}
	f, err := fifo.OpenFifo(context.Background(), stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
	if err != nil {
		return fmt.Errorf("containerd-shim: opening %s failed: %s", stdin, err)
	}
	cwg.Add(1)
	go func() {
		cwg.Done()
		p := bufPool.Get().(*[]byte)
		defer bufPool.Put(p)

		io.CopyBuffer(rio.Stdin(), f, *p)
		rio.Stdin().Close()
		f.Close()
	}()
	return nil
}

// countingWriteCloser masks io.Closer() until close has been invoked a certain number of times.
type countingWriteCloser struct {
	io.WriteCloser
	count int64
}

func (c *countingWriteCloser) Close() error {
	if atomic.AddInt64(&c.count, -1) > 0 {
		return nil
	}
	return c.WriteCloser.Close()
}

// isFifo checks if a file is a fifo
// if the file does not exist then it returns false
func isFifo(path string) (bool, error) {
	stat, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	if stat.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
		return true, nil
	}
	return false, nil
}

func newBinaryIO(ctx context.Context, id string, uri *url.URL) (runc.IO, error) {
	ns, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}
	var args []string
	for k, vs := range uri.Query() {
		args = append(args, k)
		if len(vs) > 0 {
			args = append(args, vs[0])
		}
	}
	ctx, cancel := context.WithCancel(ctx)
	cmd := exec.CommandContext(ctx, uri.Host, args...)
	cmd.Env = append(cmd.Env,
		"CONTAINER_ID="+id,
		"CONTAINER_NAMESPACE="+ns,
	)
	out, err := newPipe()
	if err != nil {
		cancel()
		return nil, err
	}
	serr, err := newPipe()
	if err != nil {
		cancel()
		return nil, err
	}
	r, w, err := os.Pipe()
	if err != nil {
		cancel()
		return nil, err
	}
	cmd.ExtraFiles = append(cmd.ExtraFiles, out.r, serr.r, w)
	// don't need to register this with the reaper or wait when
	// running inside a shim
	if err := cmd.Start(); err != nil {
		cancel()
		return nil, err
	}
	// close our side of the pipe after start
	if err := w.Close(); err != nil {
		cancel()
		return nil, err
	}
	// wait for the logging binary to be ready
	b := make([]byte, 1)
	if _, err := r.Read(b); err != nil && err != io.EOF {
		cancel()
		return nil, err
	}
	return &binaryIO{
		cmd:    cmd,
		cancel: cancel,
		out:    out,
		err:    serr,
	}, nil
}

type binaryIO struct {
	cmd      *exec.Cmd
	cancel   func()
	out, err *pipe
}

func (b *binaryIO) CloseAfterStart() (err error) {
	for _, v := range []*pipe{
		b.out,
		b.err,
	} {
		if v != nil {
			if cerr := v.r.Close(); err == nil {
				err = cerr
			}
		}
	}
	return err
}

func (b *binaryIO) Close() (err error) {
	b.cancel()
	for _, v := range []*pipe{
		b.out,
		b.err,
	} {
		if v != nil {
			if cerr := v.Close(); err == nil {
				err = cerr
			}
		}
	}
	return err
}

func (b *binaryIO) Stdin() io.WriteCloser {
	return nil
}

func (b *binaryIO) Stdout() io.ReadCloser {
	return nil
}

func (b *binaryIO) Stderr() io.ReadCloser {
	return nil
}

func (b *binaryIO) Set(cmd *exec.Cmd) {
	if b.out != nil {
		cmd.Stdout = b.out.w
	}
	if b.err != nil {
		cmd.Stderr = b.err.w
	}
}

func newPipe() (*pipe, error) {
	r, w, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	return &pipe{
		r: r,
		w: w,
	}, nil
}

type pipe struct {
	r *os.File
	w *os.File
}

func (p *pipe) Close() error {
	err := p.w.Close()
	if rerr := p.r.Close(); err == nil {
		err = rerr
	}
	return err
}
<commit_msg>Make newBinaryIO public<commit_after>// +build !windows

/*
   Copyright The containerd Authors.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package proc

import (
	"context"
	"fmt"
	"io"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"sync/atomic"
	"syscall"

	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/namespaces"
	"github.com/containerd/containerd/runtime/proc"
	"github.com/containerd/fifo"
	runc "github.com/containerd/go-runc"
	"github.com/pkg/errors"
)

var bufPool = sync.Pool{
	New: func() interface{} {
		buffer := make([]byte, 32<<10)
		return &buffer
	},
}

type processIO struct {
	io runc.IO

	uri   *url.URL
	copy  bool
	stdio proc.Stdio
}

func (p *processIO) Close() error {
	if p.io != nil {
		return p.io.Close()
	}
	return nil
}

func (p *processIO) IO() runc.IO {
	return p.io
}

func (p *processIO) Copy(ctx context.Context, wg *sync.WaitGroup) error {
	if !p.copy {
		return nil
	}
	var cwg sync.WaitGroup
	if err := copyPipes(ctx, p.IO(), p.stdio.Stdin, p.stdio.Stdout, p.stdio.Stderr, wg, &cwg); err != nil {
		return errors.Wrap(err, "unable to copy pipes")
	}
	cwg.Wait()
	return nil
}

func createIO(ctx context.Context, id string, ioUID, ioGID int, stdio proc.Stdio) (*processIO, error) {
	pio := &processIO{
		stdio: stdio,
	}
	if stdio.IsNull() {
		i, err := runc.NewNullIO()
		if err != nil {
			return nil, err
		}
		pio.io = i
		return pio, nil
	}
	u, err := url.Parse(stdio.Stdout)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse stdout uri")
	}
	if u.Scheme == "" {
		u.Scheme = "fifo"
	}
	pio.uri = u
	switch u.Scheme {
	case "fifo":
		pio.copy = true
		pio.io, err = runc.NewPipeIO(ioUID, ioGID, withConditionalIO(stdio))
	case "binary":
		pio.io, err = NewBinaryIO(ctx, id, u)
	case "file":
		if err := os.MkdirAll(filepath.Dir(u.Host), 0755); err != nil {
			return nil, err
		}
		var f *os.File
		f, err = os.OpenFile(u.Host, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			return nil, err
		}
		f.Close()
		pio.copy = true
		pio.io, err = runc.NewPipeIO(ioUID, ioGID, withConditionalIO(stdio))
	default:
		return nil, errors.Errorf("unknown STDIO scheme %s", u.Scheme)
	}
	if err != nil {
		return nil, err
	}
	return pio, nil
}

func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error {
	var sameFile *countingWriteCloser
	for _, i := range []struct {
		name string
		dest func(wc io.WriteCloser, rc io.Closer)
	}{
		{
			name: stdout,
			dest: func(wc io.WriteCloser, rc io.Closer) {
				wg.Add(1)
				cwg.Add(1)
				go func() {
					cwg.Done()
					p := bufPool.Get().(*[]byte)
					defer bufPool.Put(p)
					if _, err := io.CopyBuffer(wc, rio.Stdout(), *p); err != nil {
						log.G(ctx).Warn("error copying stdout")
					}
					wg.Done()
					wc.Close()
					if rc != nil {
						rc.Close()
					}
				}()
			},
		}, {
			name: stderr,
			dest: func(wc io.WriteCloser, rc io.Closer) {
				wg.Add(1)
				cwg.Add(1)
				go func() {
					cwg.Done()
					p := bufPool.Get().(*[]byte)
					defer bufPool.Put(p)
					if _, err := io.CopyBuffer(wc, rio.Stderr(), *p); err != nil {
						log.G(ctx).Warn("error copying stderr")
					}
					wg.Done()
					wc.Close()
					if rc != nil {
						rc.Close()
					}
				}()
			},
		},
	} {
		ok, err := isFifo(i.name)
		if err != nil {
			return err
		}
		var (
			fw io.WriteCloser
			fr io.Closer
		)
		if ok {
			if fw, err = fifo.OpenFifo(ctx, i.name, syscall.O_WRONLY, 0); err != nil {
				return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
			}
			if fr, err = fifo.OpenFifo(ctx, i.name, syscall.O_RDONLY, 0); err != nil {
				return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
			}
		} else {
			if sameFile != nil {
				sameFile.count++
				i.dest(sameFile, nil)
				continue
			}
			if fw, err = os.OpenFile(i.name, syscall.O_WRONLY|syscall.O_APPEND, 0); err != nil {
				return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err)
			}
			if stdout == stderr {
				sameFile = &countingWriteCloser{
					WriteCloser: fw,
					count:       1,
				}
			}
		}
		i.dest(fw, fr)
	}
	if stdin == "" {
		return nil
	}
	f, err := fifo.OpenFifo(context.Background(), stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
	if err != nil {
		return fmt.Errorf("containerd-shim: opening %s failed: %s", stdin, err)
	}
	cwg.Add(1)
	go func() {
		cwg.Done()
		p := bufPool.Get().(*[]byte)
		defer bufPool.Put(p)

		io.CopyBuffer(rio.Stdin(), f, *p)
		rio.Stdin().Close()
		f.Close()
	}()
	return nil
}

// countingWriteCloser masks io.Closer() until close has been invoked a certain number of times.
type countingWriteCloser struct {
	io.WriteCloser
	count int64
}

func (c *countingWriteCloser) Close() error {
	if atomic.AddInt64(&c.count, -1) > 0 {
		return nil
	}
	return c.WriteCloser.Close()
}

// isFifo checks if a file is a fifo
// if the file does not exist then it returns false
func isFifo(path string) (bool, error) {
	stat, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	if stat.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
		return true, nil
	}
	return false, nil
}

// NewBinaryIO runs a custom binary process for pluggable shim logging
func NewBinaryIO(ctx context.Context, id string, uri *url.URL) (runc.IO, error) {
	ns, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}
	var args []string
	for k, vs := range uri.Query() {
		args = append(args, k)
		if len(vs) > 0 {
			args = append(args, vs[0])
		}
	}
	ctx, cancel := context.WithCancel(ctx)
	cmd := exec.CommandContext(ctx, uri.Host, args...)
	cmd.Env = append(cmd.Env,
		"CONTAINER_ID="+id,
		"CONTAINER_NAMESPACE="+ns,
	)
	out, err := newPipe()
	if err != nil {
		cancel()
		return nil, err
	}
	serr, err := newPipe()
	if err != nil {
		cancel()
		return nil, err
	}
	r, w, err := os.Pipe()
	if err != nil {
		cancel()
		return nil, err
	}
	cmd.ExtraFiles = append(cmd.ExtraFiles, out.r, serr.r, w)
	// don't need to register this with the reaper or wait when
	// running inside a shim
	if err := cmd.Start(); err != nil {
		cancel()
		return nil, err
	}
	// close our side of the pipe after start
	if err := w.Close(); err != nil {
		cancel()
		return nil, err
	}
	// wait for the logging binary to be ready
	b := make([]byte, 1)
	if _, err := r.Read(b); err != nil && err != io.EOF {
		cancel()
		return nil, err
	}
	return &binaryIO{
		cmd:    cmd,
		cancel: cancel,
		out:    out,
		err:    serr,
	}, nil
}

type binaryIO struct {
	cmd      *exec.Cmd
	cancel   func()
	out, err *pipe
}

func (b *binaryIO) CloseAfterStart() (err error) {
	for _, v := range []*pipe{
		b.out,
		b.err,
	} {
		if v != nil {
			if cerr := v.r.Close(); err == nil {
				err = cerr
			}
		}
	}
	return err
}

func (b *binaryIO) Close() (err error) {
	b.cancel()
	for _, v := range []*pipe{
		b.out,
		b.err,
	} {
		if v != nil {
			if cerr := v.Close(); err == nil {
				err = cerr
			}
		}
	}
	return err
}

func (b *binaryIO) Stdin() io.WriteCloser {
	return nil
}

func (b *binaryIO) Stdout() io.ReadCloser {
	return nil
}

func (b *binaryIO) Stderr() io.ReadCloser {
	return nil
}

func (b *binaryIO) Set(cmd *exec.Cmd) {
	if b.out != nil {
		cmd.Stdout = b.out.w
	}
	if b.err != nil {
		cmd.Stderr = b.err.w
	}
}

func newPipe() (*pipe, error) {
	r, w, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	return &pipe{
		r: r,
		w: w,
	}, nil
}

type pipe struct {
	r *os.File
	w *os.File
}

func (p *pipe) Close() error {
	err := p.w.Close()
	if rerr := p.r.Close(); err == nil {
		err = rerr
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"

	"github.com/fabiofalci/sconsify/spotify"
	"github.com/jroimartin/gocui"
)

func nextView(g *gocui.Gui, v *gocui.View) error {
	currentView := g.CurrentView()
	if currentView == nil || currentView.Name() == "side" {
		return g.SetCurrentView("main")
	}
	return g.SetCurrentView("side")
}

func cursorDown(g *gocui.Gui, v *gocui.View) error {
	if v != nil {
		cx, cy := v.Cursor()
		if err := v.SetCursor(cx, cy+1); err != nil {
			ox, oy := v.Origin()
			if err := v.SetOrigin(ox, oy+1); err != nil {
				return err
			}
		}
	}
	return nil
}

func cursorUp(g *gocui.Gui, v *gocui.View) error {
	if v != nil {
		ox, oy := v.Origin()
		cx, cy := v.Cursor()
		if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
			if err := v.SetOrigin(ox, oy-1); err != nil {
				return err
			}
		}
	}
	return nil
}

func getPlaylist(g *gocui.Gui, v *gocui.View) error {
	var l string
	var err error

	_, cy := v.Cursor()
	if l, err = v.Line(cy); err != nil {
		l = ""
	}

	maxX, maxY := g.Size()
	if v, err := g.SetView("msg", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
		if err != gocui.ErrorUnkView {
			return err
		}
		fmt.Fprintln(v, l)
		if err := g.SetCurrentView("msg"); err != nil {
			return err
		}
	}
	return nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrorQuit
}

func keybindings(g *gocui.Gui) error {
	if err := g.SetKeybinding("", gocui.KeyArrowDown, 0, cursorDown); err != nil {
		return err
	}
	if err := g.SetKeybinding("", gocui.KeyArrowUp, 0, cursorUp); err != nil {
		return err
	}
	if err := g.SetKeybinding("main", gocui.KeyArrowLeft, 0, nextView); err != nil {
		return err
	}
	if err := g.SetKeybinding("side", gocui.KeyArrowRight, 0, nextView); err != nil {
		return err
	}

	// vi navigation
	if err := g.SetKeybinding("", 'j', 0, cursorDown); err != nil {
		return err
	}
	if err := g.SetKeybinding("", 'k', 0, cursorUp); err != nil {
		return err
	}
	if err := g.SetKeybinding("main", 'h', 0, nextView); err != nil {
		return err
	}
	if err := g.SetKeybinding("side", 'l', 0, nextView); err != nil {
		return err
	}

	if err := g.SetKeybinding("", gocui.KeyCtrlC, 0, quit); err != nil {
		return err
	}
	if err := g.SetKeybinding("", 'q', 0, quit); err != nil {
		return err
	}

	if err := g.SetKeybinding("side", gocui.KeyEnter, 0, getPlaylist); err != nil {
		return err
	}
	if err := g.SetKeybinding("main", gocui.KeyEnter, 0, getPlaylist); err != nil {
		return err
	}

	return nil
}

func layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("side", -1, -1, 30, maxY); err != nil {
		if err != gocui.ErrorUnkView {
			return err
		}
		v.Highlight = true

		fmt.Fprintln(v, "Playlist Dummy")
		if spotify.GetSession() != nil {
			playlists, _ := spotify.GetSession().Playlists()
			// for i := 0; i < playlists.Playlists(); i++ {
			// 	playlist := playlists.Playlist(i)
			// 	playlist.Wait()
			// }
			fmt.Fprintln(v, "Playlist 1 %v", playlists.Playlists())
			fmt.Fprintln(v, "Playlist 2")
			fmt.Fprintln(v, "Playlist 3")
			fmt.Fprintln(v, "Playlist 4")
		}
	}
	if v, err := g.SetView("main", 30, -1, maxX, maxY); err != nil {
		if err != gocui.ErrorUnkView {
			return err
		}
		fmt.Fprintln(v, "Music A")
		fmt.Fprintln(v, "Music B")
		fmt.Fprintln(v, "Music C")
		fmt.Fprintln(v, "Music D")
		fmt.Fprintln(v, "Music E")
		v.Highlight = true
		if err := g.SetCurrentView("main"); err != nil {
			return err
		}
	}
	return nil
}

func main() {
	var err error

	spotify.Initialise()

	g := gocui.NewGui()
	if err := g.Init(); err != nil {
		log.Panicln(err)
	}
	defer g.Close()

	g.SetLayout(layout)
	if err := keybindings(g); err != nil {
		log.Panicln(err)
	}
	g.SelBgColor = gocui.ColorGreen
	g.SelFgColor = gocui.ColorBlack
	g.ShowCursor = true

	err = g.MainLoop()
	if err != nil && err != gocui.ErrorQuit {
		log.Panicln(err)
	}
}
<commit_msg>Showing playlists<commit_after>package main

import (
	"fmt"
	"log"

	"github.com/fabiofalci/sconsify/spotify"
	"github.com/jroimartin/gocui"
	sp "github.com/op/go-libspotify/spotify"
)

func nextView(g *gocui.Gui, v *gocui.View) error {
	currentView := g.CurrentView()
	if currentView == nil || currentView.Name() == "side" {
		return g.SetCurrentView("main")
	}
	return g.SetCurrentView("side")
}

func cursorDown(g *gocui.Gui, v *gocui.View) error {
	if v != nil {
		cx, cy := v.Cursor()
		if err := v.SetCursor(cx, cy+1); err != nil {
			ox, oy := v.Origin()
			if err := v.SetOrigin(ox, oy+1); err != nil {
				return err
			}
		}
	}
	return nil
}

func cursorUp(g *gocui.Gui, v *gocui.View) error {
	if v != nil {
		ox, oy := v.Origin()
		cx, cy := v.Cursor()
		if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
			if err := v.SetOrigin(ox, oy-1); err != nil {
				return err
			}
		}
	}
	return nil
}

func getPlaylist(g *gocui.Gui, v *gocui.View) (string, error) {
	var l string
	var err error

	_, cy := v.Cursor()
	if l, err = v.Line(cy); err != nil {
		l = ""
	}

	return l, nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrorQuit
}

func keybindings(g *gocui.Gui) error {
	if err := g.SetKeybinding("", gocui.KeyArrowDown, 0, cursorDown); err != nil {
		return err
	}
	if err := g.SetKeybinding("", gocui.KeyArrowUp, 0, cursorUp); err != nil {
		return err
	}
	if err := g.SetKeybinding("main", gocui.KeyArrowLeft, 0, nextView); err != nil {
		return err
	}
	if err := g.SetKeybinding("side", gocui.KeyArrowRight, 0, nextView); err != nil {
		return err
	}

	// vi navigation
	if err := g.SetKeybinding("", 'j', 0, cursorDown); err != nil {
		return err
	}
	if err := g.SetKeybinding("", 'k', 0, cursorUp); err != nil {
		return err
	}
	if err := g.SetKeybinding("main", 'h', 0, nextView); err != nil {
		return err
	}
	if err := g.SetKeybinding("side", 'l', 0, nextView); err != nil {
		return err
	}

	if err := g.SetKeybinding("", gocui.KeyCtrlC, 0, quit); err != nil {
		return err
	}
	if err := g.SetKeybinding("", 'q', 0, quit); err != nil {
		return err
	}

	// if err := g.SetKeybinding("side", gocui.KeyEnter, 0, getPlaylist); err != nil {
	// 	return err
	// }
	// if err := g.SetKeybinding("main", gocui.KeyEnter, 0, getPlaylist); err != nil {
	// 	return err
	// }

	return nil
}

func layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("side", -1, -1, 30, maxY); err != nil {
		if err != gocui.ErrorUnkView {
			return err
		}
		sideView = v
		sideView.Highlight = true

		if spotify.GetSession() != nil {
			playlists, _ := spotify.GetSession().Playlists()
			for i := 0; i < playlists.Playlists(); i++ {
				playlist := playlists.Playlist(i)
				playlist.Wait()
				fmt.Fprintln(v, playlist.Name())
			}
		}
	}
	if v, err := g.SetView("main", 30, -1, maxX, maxY); err != nil {
		if err != gocui.ErrorUnkView {
			return err
		}

		currentPlaylist, err := getPlaylist(g, sideView)
		if err == nil && playlistsMap != nil {
			playlist := playlistsMap[currentPlaylist]

			if playlist != nil {
				playlist.Wait()
				for i := 0; i < playlist.Tracks(); i++ {
					playlistTrack := playlist.Track(i)
					track := playlistTrack.Track()
					track.Wait()
					fmt.Fprintf(v, "%v", track.Name())
					// track.Wait()
					// fmt.Fprintf(v, "%v", track.Name())
				}
			}

		}
		v.Highlight = true
		if err := g.SetCurrentView("main"); err != nil {
			return err
		}
	}
	return nil
}

var sideView *gocui.View

var (
	playlistsMap = make(map[string]*sp.Playlist)
)

func main() {
	var err error

	spotify.Initialise()

	if spotify.GetSession() != nil {
		playlists, _ := spotify.GetSession().Playlists()
		playlists.Wait()
		for i := 0; i < playlists.Playlists(); i++ {
			playlist := playlists.Playlist(i)
			playlist.Wait()

			if playlists.PlaylistType(i) == sp.PlaylistTypePlaylist {
				playlistsMap[playlist.Name()] = playlist
			}
		}
	}

	g := gocui.NewGui()
	if err := g.Init(); err != nil {
		log.Panicln(err)
	}
	defer g.Close()

	g.SetLayout(layout)
	if err := keybindings(g); err != nil {
		log.Panicln(err)
	}
	g.SelBgColor = gocui.ColorGreen
	g.SelFgColor = gocui.ColorBlack
	g.ShowCursor = true

	err = g.MainLoop()
	if err != nil && err != gocui.ErrorQuit {
		log.Panicln(err)
	}
}
<|endoftext|>


<commit_before>package discordbottemp

import (
	"fmt"
	"log"
	"time"

	"github.com/antihax/evedata/internal/datapackages"
	"github.com/antihax/evedata/internal/gobcoder"
	nsq "github.com/nsqio/go-nsq"
	yaml "gopkg.in/yaml.v2"
)

func init() {
	addHandler("characterNotifications", spawnCharacterNotificationsConsumer)
}

func spawnCharacterNotificationsConsumer(s *DiscordBot, consumer *nsq.Consumer) {
	consumer.AddHandler(s.wait(nsq.HandlerFunc(s.characterNotificationsHandler)))
}

func (s *DiscordBot) characterNotificationsHandler(message *nsq.Message) error {
	notifications := datapackages.CharacterNotifications{}
	err := gobcoder.GobDecoder(message.Body, &notifications)
	if err != nil {
		log.Println(err)
		return err
	}
	if len(notifications.Notifications) == 0 {
		return nil
	}

	// Only process contracts.
	if notifications.TokenCharacterID == 1962167517 || notifications.TokenCharacterID == 94135910 {
		for _, n := range notifications.Notifications {
			// Skip the notification if if is more than three hours old

			if n.Timestamp.Before(time.Now().Add(-time.Hour * 6)) {
				continue
			}

			if s.outQueue.CheckWorkCompleted(fmt.Sprintf("evedata-bot-notification-sent:%d", 99002974), n.NotificationId) {
				continue
			}

			err := s.checkNotification(n.Type_, n.Text, n.Timestamp)
			if err != nil {
				continue
			}

			s.outQueue.SetWorkCompleted(fmt.Sprintf("evedata-bot-notification-sent:%d", 99002974), n.NotificationId)
		}
	}
	return nil
}

// AllWarDeclaredMsg message
type AllWarDeclaredMsg struct {
	AgainstID    int64   `yaml:"againstID"`
	Cost         float64 `yaml:"cost"`
	DeclaredByID int64   `yaml:"declaredByID"`
	DelayHours   int64   `yaml:"delayHours"`
	HostileState int64   `yaml:"hostileState"`
}

// OrbitalAttacked message
type OrbitalAttacked struct {
	AggressorAllianceID int64   `yaml:"aggressorAllianceID"`
	AggressorCorpID     int64   `yaml:"aggressorCorpID"`
	PlanetID            int64   `yaml:"planetID"`
	MoonID              int64   `yaml:"moonID"`
	ShieldLevel         float64 `yaml:"shieldLevel"`
	ArmorValue          float64 `yaml:"armorValue"`
	HullValue           float64 `yaml:"hullValue"`
	TypeID              int64   `yaml:"typeID"`
	SolarSystemID       int64   `yaml:"solarSystemID"`
}

// OrbitalReinforced message
type OrbitalReinforced struct {
	AggressorAllianceID int64 `yaml:"aggressorAllianceID"`
	AggressorCorpID     int64 `yaml:"aggressorCorpID"`
	PlanetID            int64 `yaml:"planetID"`
	MoonID              int64 `yaml:"moonID"`
	ReinforceExitTime   int64 `yaml:"reinforceExitTime"`
	TypeID              int64 `yaml:"typeID"`
	SolarSystemID       int64 `yaml:"solarSystemID"`
}

func (s *DiscordBot) checkNotification(notificationType, text string, timestamp time.Time) error {

	switch notificationType {

	case "AllWarDeclaredMsg", "CorpWarDeclaredMsg":
		l := AllWarDeclaredMsg{}
		err := yaml.Unmarshal([]byte(text), &l)
		if err != nil {
			return err
		}

		defender, _ := s.getEntityName(l.AgainstID)
		attacker, _ := s.getEntityName(l.DeclaredByID)

		sendNotificationMessage(fmt.Sprintf("@everyone [%s] [%s](https://www.evedata.org/%s?id=%d) just declared war on [%s](https://www.evedata.org/%s?id=%d)\n",
			timestamp.UTC().String(), attacker.Name, attacker.EntityType, l.DeclaredByID, defender.Name, defender.EntityType, l.AgainstID))
	case "StructureUnderAttack", "OrbitalAttacked", "TowerAlertMsg":
		l := OrbitalAttacked{}
		yaml.Unmarshal([]byte(text), &l)

		location := int64(0)
		if l.MoonID > 0 {
			location = l.MoonID
		} else if l.PlanetID > 0 {
			location = l.PlanetID
		}

		attacker := int64(0)
		attackerType := ""
		if l.AggressorAllianceID > 0 {
			attacker = l.AggressorAllianceID
			attackerType = "alliance"
		} else if l.AggressorCorpID > 0 {
			attacker = l.AggressorCorpID
			attackerType = "corporation"
		}

		locationName, _ := s.getCelestialName(location)
		systemName, _ := s.getCelestialName(l.SolarSystemID)
		structureType, _ := s.getTypeName(l.TypeID)
		attackerName, _ := s.getEntityName(attacker)

		return sendNotificationMessage(fmt.Sprintf("@everyone [%s] %s is under attack at %s in %s by [%s](https://www.evedata.org/%s?id=%d) S: %.1f%%  A: %.1f%%  H: %.1f%% \n",
			timestamp.UTC().String(), structureType, locationName, systemName, attackerName.Name, attackerType, attacker, l.ShieldLevel*100, l.ArmorValue*100, l.HullValue*100))

	case "OrbitalReinforced":
		l := OrbitalReinforced{}
		yaml.Unmarshal([]byte(text), &l)

		location := int64(0)
		if l.MoonID > 0 {
			location = l.MoonID
		} else if l.PlanetID > 0 {
			location = l.PlanetID
		}

		attacker := int64(0)
		attackerType := ""
		if l.AggressorAllianceID > 0 {
			attacker = l.AggressorAllianceID
			attackerType = "alliance"
		} else if l.AggressorCorpID > 0 {
			attacker = l.AggressorCorpID
			attackerType = "corporation"
		}

		locationName, _ := s.getCelestialName(location)
		systemName, _ := s.getCelestialName(l.SolarSystemID)
		structureType, _ := s.getTypeName(l.TypeID)
		attackerName, _ := s.getEntityName(attacker)

		return sendNotificationMessage(fmt.Sprintf("@everyone [%s] %s was reinforced at %s in %s by [%s](https://www.evedata.org/%s?id=%d). Timer expires at %s\n",
			timestamp.UTC().String(), structureType, locationName, systemName, attackerName.Name, attackerType, attacker,
			time.Unix(datapackages.WintoUnixTimestamp(l.ReinforceExitTime), 0).String()))
	}
	return nil
}

type EntityName struct {
	Name       string `db:"name" json:"name"`
	EntityType string `db:"type" json:"type"`
}

// Obtain entity name and type by ID.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getEntityName(id int64) (*EntityName, error) {
	ref := EntityName{}
	if err := s.db.QueryRowx(`
		SELECT name, 'corporation' AS type FROM evedata.corporations WHERE corporationID = ?
		UNION
		SELECT name, 'alliance' AS type FROM evedata.alliances WHERE allianceID = ?
		LIMIT 1`, id, id).StructScan(&ref); err != nil {
		return nil, err
	}
	return &ref, nil
}

// Obtain type name.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getTypeName(id int64) (string, error) {
	ref := ""
	if err := s.db.QueryRowx(`
		SELECT typeName FROM invTypes WHERE typeID = ?
		LIMIT 1`, id).Scan(&ref); err != nil {
		return "", err
	}
	return ref, nil
}

// Obtain SolarSystem name.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getSystemName(id int64) (string, error) {
	ref := ""
	if err := s.db.QueryRowx(`
		SELECT solarSystemName FROM mapSolarSystems WHERE solarSystemID = ?
		LIMIT 1`, id).Scan(&ref); err != nil {
		return "", err
	}
	return ref, nil
}

// Obtain Celestial name.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getCelestialName(id int64) (string, error) {
	ref := ""
	if err := s.db.QueryRowx(`
		SELECT itemName FROM mapDenormalize WHERE itemID = ?
		LIMIT 1`, id).Scan(&ref); err != nil {
		return "", err
	}
	return ref, nil
}
<commit_msg>Test for errors.<commit_after>package discordbottemp

import (
	"fmt"
	"log"
	"time"

	"github.com/antihax/evedata/internal/datapackages"
	"github.com/antihax/evedata/internal/gobcoder"
	nsq "github.com/nsqio/go-nsq"
	yaml "gopkg.in/yaml.v2"
)

func init() {
	addHandler("characterNotifications", spawnCharacterNotificationsConsumer)
}

func spawnCharacterNotificationsConsumer(s *DiscordBot, consumer *nsq.Consumer) {
	consumer.AddHandler(s.wait(nsq.HandlerFunc(s.characterNotificationsHandler)))
}

func (s *DiscordBot) characterNotificationsHandler(message *nsq.Message) error {
	notifications := datapackages.CharacterNotifications{}
	err := gobcoder.GobDecoder(message.Body, &notifications)
	if err != nil {
		log.Println(err)
		return err
	}
	if len(notifications.Notifications) == 0 {
		return nil
	}

	// Only process contracts.
	if notifications.TokenCharacterID == 1962167517 || notifications.TokenCharacterID == 94135910 {
		for _, n := range notifications.Notifications {
			// Skip the notification if if is more than three hours old

			if n.Timestamp.Before(time.Now().Add(-time.Hour * 6)) {
				continue
			}

			if s.outQueue.CheckWorkCompleted(fmt.Sprintf("evedata-bot-notification-sent:%d", 99002974), n.NotificationId) {
				continue
			}

			err := s.checkNotification(n.Type_, n.Text, n.Timestamp)
			if err != nil {
				continue
			}

			s.outQueue.SetWorkCompleted(fmt.Sprintf("evedata-bot-notification-sent:%d", 99002974), n.NotificationId)
		}
	}
	return nil
}

// AllWarDeclaredMsg message
type AllWarDeclaredMsg struct {
	AgainstID    int64   `yaml:"againstID"`
	Cost         float64 `yaml:"cost"`
	DeclaredByID int64   `yaml:"declaredByID"`
	DelayHours   int64   `yaml:"delayHours"`
	HostileState int64   `yaml:"hostileState"`
}

// OrbitalAttacked message
type OrbitalAttacked struct {
	AggressorAllianceID int64   `yaml:"aggressorAllianceID"`
	AggressorCorpID     int64   `yaml:"aggressorCorpID"`
	PlanetID            int64   `yaml:"planetID"`
	MoonID              int64   `yaml:"moonID"`
	ShieldLevel         float64 `yaml:"shieldLevel"`
	ArmorValue          float64 `yaml:"armorValue"`
	HullValue           float64 `yaml:"hullValue"`
	TypeID              int64   `yaml:"typeID"`
	SolarSystemID       int64   `yaml:"solarSystemID"`
}

// OrbitalReinforced message
type OrbitalReinforced struct {
	AggressorAllianceID int64 `yaml:"aggressorAllianceID"`
	AggressorCorpID     int64 `yaml:"aggressorCorpID"`
	PlanetID            int64 `yaml:"planetID"`
	MoonID              int64 `yaml:"moonID"`
	ReinforceExitTime   int64 `yaml:"reinforceExitTime"`
	TypeID              int64 `yaml:"typeID"`
	SolarSystemID       int64 `yaml:"solarSystemID"`
}

func (s *DiscordBot) checkNotification(notificationType, text string, timestamp time.Time) error {

	switch notificationType {

	case "AllWarDeclaredMsg", "CorpWarDeclaredMsg":
		l := AllWarDeclaredMsg{}
		err := yaml.Unmarshal([]byte(text), &l)
		if err != nil {
			return err
		}

		defender, err := s.getEntityName(l.AgainstID)
		if err != nil {
			return err
		}
		attacker, err := s.getEntityName(l.DeclaredByID)
		if err != nil {
			return err
		}

		sendNotificationMessage(fmt.Sprintf("@everyone [%s] [%s](https://www.evedata.org/%s?id=%d) just declared war on [%s](https://www.evedata.org/%s?id=%d)\n",
			timestamp.UTC().String(), attacker.Name, attacker.EntityType, l.DeclaredByID, defender.Name, defender.EntityType, l.AgainstID))
	case "StructureUnderAttack", "OrbitalAttacked", "TowerAlertMsg":
		l := OrbitalAttacked{}
		yaml.Unmarshal([]byte(text), &l)

		location := int64(0)
		if l.MoonID > 0 {
			location = l.MoonID
		} else if l.PlanetID > 0 {
			location = l.PlanetID
		}

		attacker := int64(0)
		attackerType := ""
		if l.AggressorAllianceID > 0 {
			attacker = l.AggressorAllianceID
			attackerType = "alliance"
		} else if l.AggressorCorpID > 0 {
			attacker = l.AggressorCorpID
			attackerType = "corporation"
		}

		locationName, err := s.getCelestialName(location)
		if err != nil {
			return err
		}
		systemName, err := s.getCelestialName(l.SolarSystemID)
		if err != nil {
			return err
		}
		structureType, err := s.getTypeName(l.TypeID)
		if err != nil {
			return err
		}
		attackerName, err := s.getEntityName(attacker)
		if err != nil {
			return err
		}

		return sendNotificationMessage(fmt.Sprintf("@everyone [%s] %s is under attack at %s in %s by [%s](https://www.evedata.org/%s?id=%d) S: %.1f%%  A: %.1f%%  H: %.1f%% \n",
			timestamp.UTC().String(), structureType, locationName, systemName, attackerName.Name, attackerType, attacker, l.ShieldLevel*100, l.ArmorValue*100, l.HullValue*100))

	case "OrbitalReinforced":
		l := OrbitalReinforced{}
		yaml.Unmarshal([]byte(text), &l)

		location := int64(0)
		if l.MoonID > 0 {
			location = l.MoonID
		} else if l.PlanetID > 0 {
			location = l.PlanetID
		}

		attacker := int64(0)
		attackerType := ""
		if l.AggressorAllianceID > 0 {
			attacker = l.AggressorAllianceID
			attackerType = "alliance"
		} else if l.AggressorCorpID > 0 {
			attacker = l.AggressorCorpID
			attackerType = "corporation"
		}

		locationName, err := s.getCelestialName(location)
		if err != nil {
			return err
		}
		systemName, err := s.getCelestialName(l.SolarSystemID)
		if err != nil {
			return err
		}
		structureType, err := s.getTypeName(l.TypeID)
		if err != nil {
			return err
		}
		attackerName, err := s.getEntityName(attacker)
		if err != nil {
			return err
		}

		return sendNotificationMessage(fmt.Sprintf("@everyone [%s] %s was reinforced at %s in %s by [%s](https://www.evedata.org/%s?id=%d). Timer expires at %s\n",
			timestamp.UTC().String(), structureType, locationName, systemName, attackerName.Name, attackerType, attacker,
			time.Unix(datapackages.WintoUnixTimestamp(l.ReinforceExitTime), 0).String()))
	}
	return nil
}

type EntityName struct {
	Name       string `db:"name" json:"name"`
	EntityType string `db:"type" json:"type"`
}

// Obtain entity name and type by ID.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getEntityName(id int64) (*EntityName, error) {
	ref := EntityName{}
	if err := s.db.QueryRowx(`
		SELECT name, 'corporation' AS type FROM evedata.corporations WHERE corporationID = ?
		UNION
		SELECT name, 'alliance' AS type FROM evedata.alliances WHERE allianceID = ?
		LIMIT 1`, id, id).StructScan(&ref); err != nil {
		return nil, err
	}
	return &ref, nil
}

// Obtain type name.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getTypeName(id int64) (string, error) {
	ref := ""
	if err := s.db.QueryRowx(`
		SELECT typeName FROM invTypes WHERE typeID = ?
		LIMIT 1`, id).Scan(&ref); err != nil {
		return "", err
	}
	return ref, nil
}

// Obtain SolarSystem name.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getSystemName(id int64) (string, error) {
	ref := ""
	if err := s.db.QueryRowx(`
		SELECT solarSystemName FROM mapSolarSystems WHERE solarSystemID = ?
		LIMIT 1`, id).Scan(&ref); err != nil {
		return "", err
	}
	return ref, nil
}

// Obtain Celestial name.
// [BENCHMARK] 0.000 sec / 0.000 sec
func (s *DiscordBot) getCelestialName(id int64) (string, error) {
	ref := ""
	if err := s.db.QueryRowx(`
		SELECT itemName FROM mapDenormalize WHERE itemID = ?
		LIMIT 1`, id).Scan(&ref); err != nil {
		return "", err
	}
	return ref, nil
}
<|endoftext|>


<commit_before>package detailed

import (
	"fmt"

	"github.com/ugorji/go/codec"

	"github.com/weaveworks/scope/probe/docker"
	"github.com/weaveworks/scope/probe/host"
	"github.com/weaveworks/scope/probe/kubernetes"
	"github.com/weaveworks/scope/probe/process"
	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/report"
)

// NodeSummaryGroup is a topology-typed group of children for a Node.
type NodeSummaryGroup struct {
	Label      string        `json:"label"`
	Nodes      []NodeSummary `json:"nodes"`
	TopologyID string        `json:"topologyId"`
	Columns    []Column      `json:"columns"`
}

// Copy returns a value copy of the NodeSummaryGroup
func (g NodeSummaryGroup) Copy() NodeSummaryGroup {
	result := NodeSummaryGroup{
		TopologyID: g.TopologyID,
		Label:      g.Label,
		Columns:    g.Columns,
	}
	for _, node := range g.Nodes {
		result.Nodes = append(result.Nodes, node.Copy())
	}
	return result
}

// Column provides special json serialization for column ids, so they include
// their label for the frontend.
// codecgen: skip
type Column string

// CodecEncodeSelf implements codec.Selfer
func (c *Column) CodecEncodeSelf(encoder *codec.Encoder) {
	in := map[string]string{"id": string(*c), "label": Label(string(*c))}
	encoder.Encode(in)
}

// CodecDecodeSelf implements codec.Selfer
func (c *Column) CodecDecodeSelf(decoder *codec.Decoder) {
	m := map[string]string{}
	decoder.Decode(&m)
	*c = Column(m["id"])
}

// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
func (Column) MarshalJSON() ([]byte, error) {
	panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
}

// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
func (*Column) UnmarshalJSON(b []byte) error {
	panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
}

// NodeSummary is summary information about a child for a Node.
type NodeSummary struct {
	ID           string        `json:"id"`
	Label        string        `json:"label"`
	Linkable     bool          `json:"linkable"` // Whether this node can be linked-to
	Metadata     []MetadataRow `json:"metadata,omitempty"`
	DockerLabels []MetadataRow `json:"docker_labels,omitempty"`
	Metrics      []MetricRow   `json:"metrics,omitempty"`
}

// MakeNodeSummary summarizes a node, if possible.
func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
	renderers := map[string]func(report.Node) NodeSummary{
		report.Process:        processNodeSummary,
		report.Container:      containerNodeSummary,
		report.ContainerImage: containerImageNodeSummary,
		report.Pod:            podNodeSummary,
		report.Host:           hostNodeSummary,
	}
	if renderer, ok := renderers[n.Topology]; ok {
		return renderer(n), true
	}
	return NodeSummary{}, false
}

// Copy returns a value copy of the NodeSummary
func (n NodeSummary) Copy() NodeSummary {
	result := NodeSummary{
		ID:       n.ID,
		Label:    n.Label,
		Linkable: n.Linkable,
	}
	for _, row := range n.Metadata {
		result.Metadata = append(result.Metadata, row.Copy())
	}
	for _, row := range n.DockerLabels {
		result.DockerLabels = append(result.DockerLabels, row.Copy())
	}
	for _, row := range n.Metrics {
		result.Metrics = append(result.Metrics, row.Copy())
	}
	return result
}

func baseNodeSummary(id, label string, linkable bool, nmd report.Node) NodeSummary {
	return NodeSummary{
		ID:           id,
		Label:        label,
		Linkable:     linkable,
		Metadata:     NodeMetadata(nmd),
		DockerLabels: NodeDockerLabels(nmd),
		Metrics:      NodeMetrics(nmd),
	}
}

func processNodeSummary(nmd report.Node) NodeSummary {
	var (
		id               string
		label, nameFound = nmd.Latest.Lookup(process.Name)
	)
	if pid, ok := nmd.Latest.Lookup(process.PID); ok {
		if !nameFound {
			label = fmt.Sprintf("(%s)", pid)
		}
		id = render.MakeProcessID(report.ExtractHostID(nmd), pid)
	}
	_, isConnected := nmd.Latest.Lookup(render.IsConnected)
	return baseNodeSummary(id, label, isConnected, nmd)
}

func containerNodeSummary(nmd report.Node) NodeSummary {
	label, _ := render.GetRenderableContainerName(nmd)
	containerID, _ := nmd.Latest.Lookup(docker.ContainerID)
	return baseNodeSummary(render.MakeContainerID(containerID), label, true, nmd)
}

func containerImageNodeSummary(nmd report.Node) NodeSummary {
	imageName, _ := nmd.Latest.Lookup(docker.ImageName)
	return baseNodeSummary(render.MakeContainerImageID(render.ImageNameWithoutVersion(imageName)), imageName, true, nmd)
}

func podNodeSummary(nmd report.Node) NodeSummary {
	podID, _ := nmd.Latest.Lookup(kubernetes.PodID)
	podName, _ := nmd.Latest.Lookup(kubernetes.PodName)
	return baseNodeSummary(render.MakePodID(podID), podName, true, nmd)
}

func hostNodeSummary(nmd report.Node) NodeSummary {
	hostName, _ := nmd.Latest.Lookup(host.HostName)
	return baseNodeSummary(render.MakeHostID(hostName), hostName, true, nmd)
}

type nodeSummariesByID []NodeSummary

func (s nodeSummariesByID) Len() int           { return len(s) }
func (s nodeSummariesByID) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s nodeSummariesByID) Less(i, j int) bool { return s[i].ID < s[j].ID }
<commit_msg>Circumvent https://github.com/ugorji/go/issues/142<commit_after>package detailed

import (
	"fmt"

	"github.com/ugorji/go/codec"

	"github.com/weaveworks/scope/probe/docker"
	"github.com/weaveworks/scope/probe/host"
	"github.com/weaveworks/scope/probe/kubernetes"
	"github.com/weaveworks/scope/probe/process"
	"github.com/weaveworks/scope/render"
	"github.com/weaveworks/scope/report"
)

// NodeSummaryGroup is a topology-typed group of children for a Node.
// Skip codec-generation for this type to circumvent render/detailed/summary.go
// codecgen: skip
type NodeSummaryGroup struct {
	Label      string        `json:"label"`
	Nodes      []NodeSummary `json:"nodes"`
	TopologyID string        `json:"topologyId"`
	Columns    []Column      `json:"columns"`
}

// Copy returns a value copy of the NodeSummaryGroup
func (g NodeSummaryGroup) Copy() NodeSummaryGroup {
	result := NodeSummaryGroup{
		TopologyID: g.TopologyID,
		Label:      g.Label,
		Columns:    g.Columns,
	}
	for _, node := range g.Nodes {
		result.Nodes = append(result.Nodes, node.Copy())
	}
	return result
}

// Column provides special json serialization for column ids, so they include
// their label for the frontend.
// codecgen: skip
type Column string

// CodecEncodeSelf implements codec.Selfer
func (c *Column) CodecEncodeSelf(encoder *codec.Encoder) {
	in := map[string]string{"id": string(*c), "label": Label(string(*c))}
	encoder.Encode(in)
}

// CodecDecodeSelf implements codec.Selfer
func (c *Column) CodecDecodeSelf(decoder *codec.Decoder) {
	m := map[string]string{}
	decoder.Decode(&m)
	*c = Column(m["id"])
}

// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
func (Column) MarshalJSON() ([]byte, error) {
	panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
}

// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
func (*Column) UnmarshalJSON(b []byte) error {
	panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
}

// NodeSummary is summary information about a child for a Node.
type NodeSummary struct {
	ID           string        `json:"id"`
	Label        string        `json:"label"`
	Linkable     bool          `json:"linkable"` // Whether this node can be linked-to
	Metadata     []MetadataRow `json:"metadata,omitempty"`
	DockerLabels []MetadataRow `json:"docker_labels,omitempty"`
	Metrics      []MetricRow   `json:"metrics,omitempty"`
}

// MakeNodeSummary summarizes a node, if possible.
func MakeNodeSummary(n report.Node) (NodeSummary, bool) {
	renderers := map[string]func(report.Node) NodeSummary{
		report.Process:        processNodeSummary,
		report.Container:      containerNodeSummary,
		report.ContainerImage: containerImageNodeSummary,
		report.Pod:            podNodeSummary,
		report.Host:           hostNodeSummary,
	}
	if renderer, ok := renderers[n.Topology]; ok {
		return renderer(n), true
	}
	return NodeSummary{}, false
}

// Copy returns a value copy of the NodeSummary
func (n NodeSummary) Copy() NodeSummary {
	result := NodeSummary{
		ID:       n.ID,
		Label:    n.Label,
		Linkable: n.Linkable,
	}
	for _, row := range n.Metadata {
		result.Metadata = append(result.Metadata, row.Copy())
	}
	for _, row := range n.DockerLabels {
		result.DockerLabels = append(result.DockerLabels, row.Copy())
	}
	for _, row := range n.Metrics {
		result.Metrics = append(result.Metrics, row.Copy())
	}
	return result
}

func baseNodeSummary(id, label string, linkable bool, nmd report.Node) NodeSummary {
	return NodeSummary{
		ID:           id,
		Label:        label,
		Linkable:     linkable,
		Metadata:     NodeMetadata(nmd),
		DockerLabels: NodeDockerLabels(nmd),
		Metrics:      NodeMetrics(nmd),
	}
}

func processNodeSummary(nmd report.Node) NodeSummary {
	var (
		id               string
		label, nameFound = nmd.Latest.Lookup(process.Name)
	)
	if pid, ok := nmd.Latest.Lookup(process.PID); ok {
		if !nameFound {
			label = fmt.Sprintf("(%s)", pid)
		}
		id = render.MakeProcessID(report.ExtractHostID(nmd), pid)
	}
	_, isConnected := nmd.Latest.Lookup(render.IsConnected)
	return baseNodeSummary(id, label, isConnected, nmd)
}

func containerNodeSummary(nmd report.Node) NodeSummary {
	label, _ := render.GetRenderableContainerName(nmd)
	containerID, _ := nmd.Latest.Lookup(docker.ContainerID)
	return baseNodeSummary(render.MakeContainerID(containerID), label, true, nmd)
}

func containerImageNodeSummary(nmd report.Node) NodeSummary {
	imageName, _ := nmd.Latest.Lookup(docker.ImageName)
	return baseNodeSummary(render.MakeContainerImageID(render.ImageNameWithoutVersion(imageName)), imageName, true, nmd)
}

func podNodeSummary(nmd report.Node) NodeSummary {
	podID, _ := nmd.Latest.Lookup(kubernetes.PodID)
	podName, _ := nmd.Latest.Lookup(kubernetes.PodName)
	return baseNodeSummary(render.MakePodID(podID), podName, true, nmd)
}

func hostNodeSummary(nmd report.Node) NodeSummary {
	hostName, _ := nmd.Latest.Lookup(host.HostName)
	return baseNodeSummary(render.MakeHostID(hostName), hostName, true, nmd)
}

type nodeSummariesByID []NodeSummary

func (s nodeSummariesByID) Len() int           { return len(s) }
func (s nodeSummariesByID) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s nodeSummariesByID) Less(i, j int) bool { return s[i].ID < s[j].ID }
<|endoftext|>


<commit_before>package mesosreporter

import (
	"fmt"
	"log"
	"os/exec"
	"strconv"

	"github.com/dropbox/changes-client/client"
	"github.com/dropbox/changes-client/client/adapter"
	"github.com/dropbox/changes-client/client/reporter"
	"github.com/dropbox/changes-client/common/sentry"
)

// A reporter that connects and reports to a specific jobstep id.
// Each jobstep id has a number of endpoints associated with it that
// allows the reporter to update the status of logs, snapshots, etc.
type Reporter struct {
	reporter.DefaultReporter
	dontPushLogChunks bool
}

func (r *Reporter) Init(c *client.Config) {
	r.dontPushLogChunks = c.GetDebugConfigBool("mesosDontPushLogChunks", false)
	r.DefaultReporter.Init(c)
}

func (r *Reporter) PushJobstepStatus(status string, result string) {
	log.Printf("[reporter] Pushing status %s", status)
	form := make(map[string]string)
	form["status"] = status
	if len(result) > 0 {
		form["result"] = result
	}

	if out, err := exec.Command("/bin/hostname", "-f").Output(); err != nil {
		sentry.Message(fmt.Sprintf("[reporter] Unable to detect hostname: %v", err), map[string]string{})
	} else {
		form["node"] = string(out)
	}
	r.PublishChannel <- reporter.ReportPayload{Path: r.JobstepAPIPath(), Data: form, Filename: ""}
}

func (r *Reporter) PushCommandStatus(cID string, status string, retCode int) {
	form := make(map[string]string)
	form["status"] = status
	if retCode >= 0 {
		form["return_code"] = strconv.Itoa(retCode)
	}
	r.PublishChannel <- reporter.ReportPayload{Path: "/commands/" + cID + "/", Data: form, Filename: ""}
}

func (r *Reporter) PushLogChunk(source string, payload []byte) bool {
	if r.dontPushLogChunks {
		return true
	}
	// logappend endpoint only works for console logs
	if source != "console" {
		return true
	}
	form := make(map[string]string)
	form["source"] = source
	form["text"] = string(payload)
	r.PublishChannel <- reporter.ReportPayload{Path: r.JobstepAPIPath() + "logappend/", Data: form, Filename: ""}
	return true
}

func (r *Reporter) PushCommandOutput(cID string, status string, retCode int, output []byte) {
	form := make(map[string]string)
	form["status"] = status
	form["output"] = string(output)
	if retCode >= 0 {
		form["return_code"] = strconv.Itoa(retCode)
	}
	r.PublishChannel <- reporter.ReportPayload{Path: "/commands/" + cID + "/", Data: form, Filename: ""}
}

func (r *Reporter) PublishArtifacts(cmd client.ConfigCmd, a adapter.Adapter, clientLog *client.Log) error {
	// The artifactstore reporter should handle all artifact publishing, so this does nothing.
	return nil
}

func New() reporter.Reporter {
	return &Reporter{}
}

func init() {
	reporter.Register("mesos", New)
}
<commit_msg>Don't push log chunks by default in the mesos Reporter<commit_after>package mesosreporter

import (
	"fmt"
	"log"
	"os/exec"
	"strconv"

	"github.com/dropbox/changes-client/client"
	"github.com/dropbox/changes-client/client/adapter"
	"github.com/dropbox/changes-client/client/reporter"
	"github.com/dropbox/changes-client/common/sentry"
)

// A reporter that connects and reports to a specific jobstep id.
// Each jobstep id has a number of endpoints associated with it that
// allows the reporter to update the status of logs, snapshots, etc.
type Reporter struct {
	reporter.DefaultReporter
	dontPushLogChunks bool
}

func (r *Reporter) Init(c *client.Config) {
	r.dontPushLogChunks = c.GetDebugConfigBool("mesosDontPushLogChunks", true)
	r.DefaultReporter.Init(c)
}

func (r *Reporter) PushJobstepStatus(status string, result string) {
	log.Printf("[reporter] Pushing status %s", status)
	form := make(map[string]string)
	form["status"] = status
	if len(result) > 0 {
		form["result"] = result
	}

	if out, err := exec.Command("/bin/hostname", "-f").Output(); err != nil {
		sentry.Message(fmt.Sprintf("[reporter] Unable to detect hostname: %v", err), map[string]string{})
	} else {
		form["node"] = string(out)
	}
	r.PublishChannel <- reporter.ReportPayload{Path: r.JobstepAPIPath(), Data: form, Filename: ""}
}

func (r *Reporter) PushCommandStatus(cID string, status string, retCode int) {
	form := make(map[string]string)
	form["status"] = status
	if retCode >= 0 {
		form["return_code"] = strconv.Itoa(retCode)
	}
	r.PublishChannel <- reporter.ReportPayload{Path: "/commands/" + cID + "/", Data: form, Filename: ""}
}

func (r *Reporter) PushLogChunk(source string, payload []byte) bool {
	if r.dontPushLogChunks {
		return true
	}
	// logappend endpoint only works for console logs
	if source != "console" {
		return true
	}
	form := make(map[string]string)
	form["source"] = source
	form["text"] = string(payload)
	r.PublishChannel <- reporter.ReportPayload{Path: r.JobstepAPIPath() + "logappend/", Data: form, Filename: ""}
	return true
}

func (r *Reporter) PushCommandOutput(cID string, status string, retCode int, output []byte) {
	form := make(map[string]string)
	form["status"] = status
	form["output"] = string(output)
	if retCode >= 0 {
		form["return_code"] = strconv.Itoa(retCode)
	}
	r.PublishChannel <- reporter.ReportPayload{Path: "/commands/" + cID + "/", Data: form, Filename: ""}
}

func (r *Reporter) PublishArtifacts(cmd client.ConfigCmd, a adapter.Adapter, clientLog *client.Log) error {
	// The artifactstore reporter should handle all artifact publishing, so this does nothing.
	return nil
}

func New() reporter.Reporter {
	return &Reporter{}
}

func init() {
	reporter.Register("mesos", New)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
)

func ownerConfirmHex(confirmHex string) error {
	if confirmHex == "" {
		return errorMissingField
	}

	statement := `
		UPDATE owners
		SET confirmedEmail=true
		WHERE ownerHex IN (
			SELECT ownerHex FROM ownerConfirmHexes
			WHERE confirmHex=$1
		);
	`
	res, err := db.Exec(statement, confirmHex)
	if err != nil {
		logger.Errorf("cannot mark user's confirmedEmail as true: %v\n", err)
		return errorInternal
	}

	count, err := res.RowsAffected()
	if err != nil {
		logger.Errorf("cannot count rows affected: %v\n", err)
		return errorInternal
	}

	if count == 0 {
		return errorNoSuchConfirmationToken
	}

	statement = `
		DELETE FROM ownerConfirmHexes
		WHERE confirmHex=$1;
	`
	_, err = db.Exec(statement, confirmHex)
	if err != nil {
		logger.Warningf("cannot remove confirmation token: %v\n", err)
		// Don't return an error because this is not critical.
	}

	return nil
}

func ownerConfirmHexHandler(w http.ResponseWriter, r *http.Request) {
	if confirmHex := r.FormValue("confirmHex"); confirmHex != "" {
		if err := ownerConfirmHex(confirmHex); err == nil {
			http.Redirect(w, r, fmt.Sprintf("%s/login?confirmed=true", os.Getenv("FRONTEND")), http.StatusTemporaryRedirect)
			return
		}
	}

	// TODO: include error message in the URL
	http.Redirect(w, r, fmt.Sprintf("%s/login?confirmed=false", os.Getenv("FRONTEND")), http.StatusTemporaryRedirect)
}
<commit_msg>owner_confirm_hex.go: fix login redirect typo<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
)

func ownerConfirmHex(confirmHex string) error {
	if confirmHex == "" {
		return errorMissingField
	}

	statement := `
		UPDATE owners
		SET confirmedEmail=true
		WHERE ownerHex IN (
			SELECT ownerHex FROM ownerConfirmHexes
			WHERE confirmHex=$1
		);
	`
	res, err := db.Exec(statement, confirmHex)
	if err != nil {
		logger.Errorf("cannot mark user's confirmedEmail as true: %v\n", err)
		return errorInternal
	}

	count, err := res.RowsAffected()
	if err != nil {
		logger.Errorf("cannot count rows affected: %v\n", err)
		return errorInternal
	}

	if count == 0 {
		return errorNoSuchConfirmationToken
	}

	statement = `
		DELETE FROM ownerConfirmHexes
		WHERE confirmHex=$1;
	`
	_, err = db.Exec(statement, confirmHex)
	if err != nil {
		logger.Warningf("cannot remove confirmation token: %v\n", err)
		// Don't return an error because this is not critical.
	}

	return nil
}

func ownerConfirmHexHandler(w http.ResponseWriter, r *http.Request) {
	if confirmHex := r.FormValue("confirmHex"); confirmHex != "" {
		if err := ownerConfirmHex(confirmHex); err == nil {
			http.Redirect(w, r, fmt.Sprintf("%s/login?confirmed=true", os.Getenv("ORIGIN")), http.StatusTemporaryRedirect)
			return
		}
	}

	// TODO: include error message in the URL
	http.Redirect(w, r, fmt.Sprintf("%s/login?confirmed=false", os.Getenv("ORIGIN")), http.StatusTemporaryRedirect)
}
<|endoftext|>


<commit_before>package logrus

import (
	"encoding/json"
	"fmt"
	"runtime"
	"strings"
	"time"
)

// Formatter generates json in logstash format.
// Logstash site: http://logstash.net/
type LogstashFormatter struct {
	Type             string // if not empty use for logstash type field.
	FileLineLogLevel Level  // level on wich filename and linenumber will write to log. Be careful it's expensive.
}

func (f *LogstashFormatter) Format(entry *Entry) ([]byte, error) {
	skip := 6 // caller skip number, default for logger
	if len(entry.Data) == 5 {
		skip = 4 // for entry
	}

	entry.Data["@version"] = 1
	entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339)

	// set message field
	_, ok := entry.Data["message"]
	if ok {
		entry.Data["fields.message"] = entry.Data["message"]
	}
	entry.Data["message"] = entry.Message

	// set level field
	_, ok = entry.Data["level"]
	if ok {
		entry.Data["fields.level"] = entry.Data["level"]
	}
	entry.Data["level"] = entry.Level.String()

	// set type field
	if f.Type != "" {
		_, ok = entry.Data["type"]
		if ok {
			entry.Data["fields.type"] = entry.Data["type"]
		}
		entry.Data["type"] = f.Type
	}

	// set file and line fields
	if f.FileLineLogLevel >= entry.Level {
		_, ok = entry.Data["file"]
		if ok {
			entry.Data["fields.file"] = entry.Data["file"]
		}
		_, ok = entry.Data["line"]
		if ok {
			entry.Data["fields.line"] = entry.Data["lin"]
		}
		_, file, line, ok := runtime.Caller(skip)
		if ok {
			if slash := strings.LastIndex(file, "/"); slash >= 0 {
				entry.Data["file"] = file[slash+1:]
			}
			entry.Data["line"] = line
		}
	}

	serialized, err := json.Marshal(entry.Data)
	if err != nil {
		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
	}
	return append(serialized, '\n'), nil
}
<commit_msg>use one source_file field with package name and line number<commit_after>package logrus

import (
	"encoding/json"
	"fmt"
	"runtime"
	"strings"
	"time"
)

// Formatter generates json in logstash format.
// Logstash site: http://logstash.net/
type LogstashFormatter struct {
	Type             string // if not empty use for logstash type field.
	FileLineLogLevel Level  // level on wich filename and linenumber will write to log. Be careful it's expensive.
}

func (f *LogstashFormatter) Format(entry *Entry) ([]byte, error) {
	skip := 6 // caller skip number, default for logger
	if len(entry.Data) == 5 {
		skip = 4 // for entry
	}

	entry.Data["@version"] = 1
	entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339)

	// set message field
	_, ok := entry.Data["message"]
	if ok {
		entry.Data["fields.message"] = entry.Data["message"]
	}
	entry.Data["message"] = entry.Message

	// set level field
	_, ok = entry.Data["level"]
	if ok {
		entry.Data["fields.level"] = entry.Data["level"]
	}
	entry.Data["level"] = entry.Level.String()

	// set type field
	if f.Type != "" {
		_, ok = entry.Data["type"]
		if ok {
			entry.Data["fields.type"] = entry.Data["type"]
		}
		entry.Data["type"] = f.Type
	}

	// set file and line fields
	if f.FileLineLogLevel >= entry.Level {
		_, ok = entry.Data["source_file"]
		if ok {
			entry.Data["fields.source_file"] = entry.Data["source_file"]
		}
		_, file, line, ok := runtime.Caller(skip)
		if ok {
			split := strings.Split(file, "/")
			if l := len(split); l > 2 {
				file = fmt.Sprintf("%s/%s:%d", split[l-2], split[l-1], line)
			}
			entry.Data["source_file"] = file
		}
	}

	serialized, err := json.Marshal(entry.Data)
	if err != nil {
		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
	}
	return append(serialized, '\n'), nil
}
<|endoftext|>