<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/lib/pq"
)

func connectToDatabase() {
	var err error
	var rows *sql.Rows
	var schema string
	var schemaVer int64

	driver := "postgres"

	connect := fmt.Sprintf("dbname='%s' user='%s' password='%s' host='%s' port='%s' sslmode='%s' connect_timeout='%s'",
		SomaCfg.Database.Name,
		SomaCfg.Database.User,
		SomaCfg.Database.Pass,
		SomaCfg.Database.Host,
		SomaCfg.Database.Port,
		SomaCfg.Database.TlsMode,
		SomaCfg.Database.Timeout,
	)

	// enable handling of infinity timestamps
	pq.EnableInfinityTs(NegTimeInf, PosTimeInf)

	conn, err = sql.Open(driver, connect)
	if err != nil {
		log.Fatal(err)
	}
	if err = conn.Ping(); err != nil {
		log.Fatal(err)
	}
	log.Print("Connected to database")
	if _, err = conn.Exec(`SET TIME ZONE 'UTC';`); err != nil {
		log.Fatal(err)
	}

	// required schema versions
	required := map[string]int64{
		"inventory": 201605060001,
		"root":      201605150001,
		"auth":      201605150002,
		"soma":      201605310001,
	}

	if rows, err = conn.Query(`
SELECT schema,
       MAX(version) AS version
FROM   public.schema_versions
GROUP  BY schema;`); err != nil {
		log.Fatal("Query db schema versions: ", err)
	}

	for rows.Next() {
		if err = rows.Scan(
			&schema,
			&schemaVer,
		); err != nil {
			log.Fatal("Schema check: ", err)
		}
		if rsv, ok := required[schema]; ok {
			if rsv != required[schema] {
				log.Fatal("Incompatible schema: ", schema, schemaVer)
			} else {
				log.Printf("DB Schema %s, version: %d", schema, schemaVer)
				delete(required, schema)
			}
		} else {
			log.Fatal("Unknown schema: ", schema)
		}
	}
	if err = rows.Err(); err != nil {
		log.Fatal("Schema check: ", err)
	}
	if len(required) != 0 {
		for s, _ := range required {
			log.Printf("Missing schema: %s", s)
		}
		log.Fatal("FATAL - database incomplete")
	}
}

func pingDatabase() {
	ticker := time.NewTicker(time.Second).C

	for {
		<-ticker
		err := conn.Ping()
		if err != nil {
			log.Print(err)
		}
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Require db schema soma/201606150001<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/lib/pq"
)

func connectToDatabase() {
	var err error
	var rows *sql.Rows
	var schema string
	var schemaVer int64

	driver := "postgres"

	connect := fmt.Sprintf("dbname='%s' user='%s' password='%s' host='%s' port='%s' sslmode='%s' connect_timeout='%s'",
		SomaCfg.Database.Name,
		SomaCfg.Database.User,
		SomaCfg.Database.Pass,
		SomaCfg.Database.Host,
		SomaCfg.Database.Port,
		SomaCfg.Database.TlsMode,
		SomaCfg.Database.Timeout,
	)

	// enable handling of infinity timestamps
	pq.EnableInfinityTs(NegTimeInf, PosTimeInf)

	conn, err = sql.Open(driver, connect)
	if err != nil {
		log.Fatal(err)
	}
	if err = conn.Ping(); err != nil {
		log.Fatal(err)
	}
	log.Print("Connected to database")
	if _, err = conn.Exec(`SET TIME ZONE 'UTC';`); err != nil {
		log.Fatal(err)
	}

	// required schema versions
	required := map[string]int64{
		"inventory": 201605060001,
		"root":      201605150001,
		"auth":      201605150002,
		"soma":      201606150001,
	}

	if rows, err = conn.Query(`
SELECT schema,
       MAX(version) AS version
FROM   public.schema_versions
GROUP  BY schema;`); err != nil {
		log.Fatal("Query db schema versions: ", err)
	}

	for rows.Next() {
		if err = rows.Scan(
			&schema,
			&schemaVer,
		); err != nil {
			log.Fatal("Schema check: ", err)
		}
		if rsv, ok := required[schema]; ok {
			if rsv != required[schema] {
				log.Fatal("Incompatible schema: ", schema, schemaVer)
			} else {
				log.Printf("DB Schema %s, version: %d", schema, schemaVer)
				delete(required, schema)
			}
		} else {
			log.Fatal("Unknown schema: ", schema)
		}
	}
	if err = rows.Err(); err != nil {
		log.Fatal("Schema check: ", err)
	}
	if len(required) != 0 {
		for s, _ := range required {
			log.Printf("Missing schema: %s", s)
		}
		log.Fatal("FATAL - database incomplete")
	}
}

func pingDatabase() {
	ticker := time.NewTicker(time.Second).C

	for {
		<-ticker
		err := conn.Ping()
		if err != nil {
			log.Print(err)
		}
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package plugin

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"net"

	"github.com/appc/cni/pkg/ipam"
	"github.com/appc/cni/pkg/skel"
	"github.com/appc/cni/pkg/types"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
	weaveapi "github.com/weaveworks/weave/api"
	"github.com/weaveworks/weave/common"
	weavenet "github.com/weaveworks/weave/net"
	ipamplugin "github.com/weaveworks/weave/plugin/ipam"
)

var (
	zeroNetwork = net.IPNet{IP: net.IPv4zero, Mask: net.IPv4Mask(0, 0, 0, 0)}
	mask32      = net.IPv4Mask(0xff, 0xff, 0xff, 0xff)
)

type CNIPlugin struct {
	weave *weaveapi.Client
}

func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin {
	return &CNIPlugin{weave: weave}
}

func loadNetConf(bytes []byte) (*NetConf, error) {
	n := &NetConf{
		BrName: weavenet.WeaveBridgeName,
	}
	if err := json.Unmarshal(bytes, n); err != nil {
		return nil, fmt.Errorf("failed to load netconf: %v", err)
	}
	return n, nil
}

func (c *CNIPlugin) getIP(ipamType string, args *skel.CmdArgs) (result *types.Result, err error) {
	// Default IPAM is Weave's own
	if ipamType == "" {
		result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
	} else {
		result, err = ipam.ExecAdd(ipamType, args.StdinData)
	}
	if err == nil && result.IP4 == nil {
		return nil, fmt.Errorf("IPAM plugin failed to allocate IP address")
	}
	return result, err
}

func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
	conf, err := loadNetConf(args.StdinData)
	if err != nil {
		return err
	}

	if conf.IsGW {
		return fmt.Errorf("Gateway functionality not supported")
	}
	if conf.IPMasq {
		return fmt.Errorf("IP Masquerading functionality not supported")
	}

	result, err := c.getIP(conf.IPAM.Type, args)
	if err != nil {
		return fmt.Errorf("unable to allocate IP address: %s", err)
	}

	// If config says nothing about routes or gateway, default one will be via the bridge
	if result.IP4.Routes == nil && result.IP4.Gateway == nil {
		bridgeIP, err := findBridgeIP(conf.BrName, result.IP4.IP)
		if err == errBridgeNoIP {
			bridgeArgs := *args
			bridgeArgs.ContainerID = "weave:expose"
			bridgeIPResult, err := c.getIP(conf.IPAM.Type, &bridgeArgs)
			if err != nil {
				return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
			}
			if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
				return fmt.Errorf("unable to assign IP address to bridge: %s", err)
			}
			bridgeIP = bridgeIPResult.IP4.IP.IP
		} else if err != nil {
			return err
		}
		result.IP4.Gateway = bridgeIP
	}

	ns, err := netns.GetFromPath(args.Netns)
	if err != nil {
		return err
	}
	defer ns.Close()

	id := args.ContainerID
	if len(id) < 5 {
		data := make([]byte, 5)
		_, err := rand.Reader.Read(data)
		if err != nil {
			return err
		}
		id = fmt.Sprintf("%x", data)
	}

	if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
		return err
	}
	if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
		return setupRoutes(link, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
	}); err != nil {
		return fmt.Errorf("error setting up routes: %s", err)
	}

	result.DNS = conf.DNS
	return result.Print()
}

func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, routes []types.Route) error {
	var err error
	if routes == nil { // If config says nothing about routes, add a default one
		if !ipnet.Contains(gw) {
			// The bridge IP is not on the same subnet; add a specific route to it
			gw32 := &net.IPNet{IP: gw, Mask: mask32}
			if err = weavenet.AddRoute(link, netlink.SCOPE_LINK, gw32, nil); err != nil {
				return err
			}
		}
		routes = []types.Route{{Dst: zeroNetwork}}
	}
	for _, r := range routes {
		if r.GW != nil {
			err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, r.GW)
		} else {
			err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, gw)
		}
		if err != nil {
			return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, name, err)
		}
	}
	return nil
}

func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
	link, err := netlink.LinkByName(bridgeName)
	if err != nil {
		return err
	}
	if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
		return fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
	}
	return nil
}

var errBridgeNoIP = fmt.Errorf("Bridge has no IP address")

func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
	netdev, err := common.GetBridgeNetDev(bridgeName)
	if err != nil {
		return nil, fmt.Errorf("Failed to get netdev for %q bridge: %s", bridgeName, err)
	}
	if len(netdev.CIDRs) == 0 {
		return nil, errBridgeNoIP
	}
	for _, cidr := range netdev.CIDRs {
		if subnet.Contains(cidr.IP) {
			return cidr.IP, nil
		}
	}
	// None in the required subnet; just return the first one
	return netdev.CIDRs[0].IP, nil
}

func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
	conf, err := loadNetConf(args.StdinData)
	if err != nil {
		return err
	}

	ns, err := netns.GetFromPath(args.Netns)
	if err != nil {
		return err
	}
	defer ns.Close()
	err = weavenet.WithNetNSUnsafe(ns, func() error {
		link, err := netlink.LinkByName(args.IfName)
		if err != nil {
			return err
		}
		return netlink.LinkDel(link)
	})
	if err != nil {
		return fmt.Errorf("error removing interface: %s", err)
	}

	// Default IPAM is Weave's own
	if conf.IPAM.Type == "" {
		err = ipamplugin.NewIpam(c.weave).Release(args)
	} else {
		err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
	}
	if err != nil {
		return fmt.Errorf("unable to release IP address: %s", err)
	}
	return nil
}

type NetConf struct {
	types.NetConf
	BrName string `json:"bridge"`
	IsGW   bool   `json:"isGateway"`
	IPMasq bool   `json:"ipMasq"`
	MTU    int    `json:"mtu"`
}
<commit_msg>Include more context in error messages from CNI operations<commit_after>package plugin

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"net"

	"github.com/appc/cni/pkg/ipam"
	"github.com/appc/cni/pkg/skel"
	"github.com/appc/cni/pkg/types"
	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
	weaveapi "github.com/weaveworks/weave/api"
	"github.com/weaveworks/weave/common"
	weavenet "github.com/weaveworks/weave/net"
	ipamplugin "github.com/weaveworks/weave/plugin/ipam"
)

var (
	zeroNetwork = net.IPNet{IP: net.IPv4zero, Mask: net.IPv4Mask(0, 0, 0, 0)}
	mask32      = net.IPv4Mask(0xff, 0xff, 0xff, 0xff)
)

type CNIPlugin struct {
	weave *weaveapi.Client
}

func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin {
	return &CNIPlugin{weave: weave}
}

func loadNetConf(bytes []byte) (*NetConf, error) {
	n := &NetConf{
		BrName: weavenet.WeaveBridgeName,
	}
	if err := json.Unmarshal(bytes, n); err != nil {
		return nil, fmt.Errorf("failed to load netconf: %v", err)
	}
	return n, nil
}

func (c *CNIPlugin) getIP(ipamType string, args *skel.CmdArgs) (result *types.Result, err error) {
	// Default IPAM is Weave's own
	if ipamType == "" {
		result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
	} else {
		result, err = ipam.ExecAdd(ipamType, args.StdinData)
	}
	if err == nil && result.IP4 == nil {
		return nil, fmt.Errorf("IPAM plugin failed to allocate IP address")
	}
	return result, err
}

func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
	conf, err := loadNetConf(args.StdinData)
	if err != nil {
		return err
	}

	if conf.IsGW {
		return fmt.Errorf("Gateway functionality not supported")
	}
	if conf.IPMasq {
		return fmt.Errorf("IP Masquerading functionality not supported")
	}

	result, err := c.getIP(conf.IPAM.Type, args)
	if err != nil {
		return fmt.Errorf("unable to allocate IP address: %s", err)
	}

	// If config says nothing about routes or gateway, default one will be via the bridge
	if result.IP4.Routes == nil && result.IP4.Gateway == nil {
		bridgeIP, err := findBridgeIP(conf.BrName, result.IP4.IP)
		if err == errBridgeNoIP {
			bridgeArgs := *args
			bridgeArgs.ContainerID = "weave:expose"
			bridgeIPResult, err := c.getIP(conf.IPAM.Type, &bridgeArgs)
			if err != nil {
				return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
			}
			if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
				return fmt.Errorf("unable to assign IP address to bridge: %s", err)
			}
			bridgeIP = bridgeIPResult.IP4.IP.IP
		} else if err != nil {
			return err
		}
		result.IP4.Gateway = bridgeIP
	}

	ns, err := netns.GetFromPath(args.Netns)
	if err != nil {
		return fmt.Errorf("error accessing namespace %q: %s", args.Netns, err)
	}
	defer ns.Close()

	id := args.ContainerID
	if len(id) < 5 {
		data := make([]byte, 5)
		_, err := rand.Reader.Read(data)
		if err != nil {
			return err
		}
		id = fmt.Sprintf("%x", data)
	}

	if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
		return err
	}
	if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
		return setupRoutes(link, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
	}); err != nil {
		return fmt.Errorf("error setting up routes: %s", err)
	}

	result.DNS = conf.DNS
	return result.Print()
}

func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, routes []types.Route) error {
	var err error
	if routes == nil { // If config says nothing about routes, add a default one
		if !ipnet.Contains(gw) {
			// The bridge IP is not on the same subnet; add a specific route to it
			gw32 := &net.IPNet{IP: gw, Mask: mask32}
			if err = weavenet.AddRoute(link, netlink.SCOPE_LINK, gw32, nil); err != nil {
				return err
			}
		}
		routes = []types.Route{{Dst: zeroNetwork}}
	}
	for _, r := range routes {
		if r.GW != nil {
			err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, r.GW)
		} else {
			err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, gw)
		}
		if err != nil {
			return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, name, err)
		}
	}
	return nil
}

func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
	link, err := netlink.LinkByName(bridgeName)
	if err != nil {
		return err
	}
	if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
		return fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
	}
	return nil
}

var errBridgeNoIP = fmt.Errorf("Bridge has no IP address")

func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
	netdev, err := common.GetBridgeNetDev(bridgeName)
	if err != nil {
		return nil, fmt.Errorf("Failed to get netdev for %q bridge: %s", bridgeName, err)
	}
	if len(netdev.CIDRs) == 0 {
		return nil, errBridgeNoIP
	}
	for _, cidr := range netdev.CIDRs {
		if subnet.Contains(cidr.IP) {
			return cidr.IP, nil
		}
	}
	// None in the required subnet; just return the first one
	return netdev.CIDRs[0].IP, nil
}

func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
	conf, err := loadNetConf(args.StdinData)
	if err != nil {
		return err
	}

	ns, err := netns.GetFromPath(args.Netns)
	if err != nil {
		return fmt.Errorf("error accessing namespace %q: %s", args.Netns, err)
	}
	defer ns.Close()
	err = weavenet.WithNetNSUnsafe(ns, func() error {
		link, err := netlink.LinkByName(args.IfName)
		if err != nil {
			return err
		}
		return netlink.LinkDel(link)
	})
	if err != nil {
		return fmt.Errorf("error removing interface %q: %s", args.IfName, err)
	}

	// Default IPAM is Weave's own
	if conf.IPAM.Type == "" {
		err = ipamplugin.NewIpam(c.weave).Release(args)
	} else {
		err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
	}
	if err != nil {
		return fmt.Errorf("unable to release IP address: %s", err)
	}
	return nil
}

type NetConf struct {
	types.NetConf
	BrName string `json:"bridge"`
	IsGW   bool   `json:"isGateway"`
	IPMasq bool   `json:"ipMasq"`
	MTU    int    `json:"mtu"`
}
<|endoftext|>


<commit_before>package messenger

import (
	"errors"
	"github.com/maciekmm/messenger-platform-go-sdk/template"
)

type SendMessage struct {
	Text       string      `json:"text,omiempty"`
	Attachment *Attachment `json:"attachment,omitempty"`
}

// Recipient describes the person who will receive the message
// Either ID or PhoneNumber has to be set
type Recipient struct {
	ID          string `json:"id,omitempty"`
	PhoneNumber string `json:"phone_number,omitempty"`
}

// NotificationType describes the behavior phone will execute after receiving the message
type NotificationType string

const (
	// NotificationTypeRegular will emit a sound/vibration and a phone notification
	NotificationTypeRegular NotificationType = "REGULAR"
	// NotificationTypeSilentPush will just emit a phone notification
	NotificationTypeSilentPush NotificationType = "SILENT_PUSH"
	// NotificationTypeNoPush will not emit sound/vibration nor a phone notification
	NotificationTypeNoPush NotificationType = "NO_PUSH"
)

type MessageQuery struct {
	Recipient        Recipient        `json:"recipient"`
	Message          SendMessage      `json:"message"`
	NotificationType NotificationType `json:"notification_type,omitempty"`
}

func (mq *MessageQuery) RecipientID(recipientID string) error {
	if mq.Recipient.PhoneNumber != "" {
		return errors.New("Only one user identification (phone or id) can be specified.")
	}
	mq.Recipient.ID = recipientID
	return nil
}

func (mq *MessageQuery) RecipientPhoneNumber(phoneNumber string, button template.Button) error {
	if mq.Recipient.ID != "" {
		return errors.New("Only one user identification (phone or id) can be specified.")
	}
	mq.Recipient.PhoneNumber = phoneNumber
	return nil
}

func (mq *MessageQuery) Notification(notification NotificationType) *MessageQuery {
	mq.NotificationType = notification
	return mq
}

func (mq *MessageQuery) Text(text string) error {
	if mq.Message.Attachment != nil && mq.Message.Attachment.Type == AttachmentTypeTemplate {
		return errors.New("Can't set both text and template.")
	}
	mq.Message.Text = text;
	return nil
}

func (mq *MessageQuery) resource(typ AttachmentType, url string) error {
	if mq.Message.Attachment.Payload != nil {
		return errors.New("Attachment already specified.")
	}
	mq.Message.Attachment.Type = typ
	mq.Message.Attachment.Payload = &Resource{URL:url}
	return nil
}

func (mq *MessageQuery) Audio(url string) error {
	return mq.resource(AttachmentTypeAudio, url)
}

func (mq *MessageQuery) Video(url string) error {
	return mq.resource(AttachmentTypeVideo, url)
}

func (mq *MessageQuery) Image(url string) error {
	return mq.resource(AttachmentTypeImage, url)
}

func (mq *MessageQuery) Template(tpl template.Template) error {
	if mq.Message.Attachment == nil {
		mq.Message.Attachment = &Attachment{}
	}
	if mq.Message.Attachment.Type != AttachmentTypeTemplate && mq.Message.Attachment.Payload != nil {
		return errors.New("Non-template attachment already specified.")
	}

	if mq.Message.Attachment.Payload == nil {
		mq.Message.Attachment.Type = AttachmentTypeTemplate
		mq.Message.Attachment.Payload = &template.Payload{}
	}

	payload := mq.Message.Attachment.Payload.(*template.Payload)

	for _,v := range payload.Elements {
		if v.Type() != tpl.Type() {
			return errors.New("All templates have to have thesame type.")
		}
	}

	payload.Elements = append(payload.Elements, tpl)
	return nil
}

<commit_msg>Added some nil checks<commit_after>package messenger

import (
	"errors"
	"github.com/maciekmm/messenger-platform-go-sdk/template"
)

type SendMessage struct {
	Text       string      `json:"text,omiempty"`
	Attachment *Attachment `json:"attachment,omitempty"`
}

// Recipient describes the person who will receive the message
// Either ID or PhoneNumber has to be set
type Recipient struct {
	ID          string `json:"id,omitempty"`
	PhoneNumber string `json:"phone_number,omitempty"`
}

// NotificationType describes the behavior phone will execute after receiving the message
type NotificationType string

const (
	// NotificationTypeRegular will emit a sound/vibration and a phone notification
	NotificationTypeRegular NotificationType = "REGULAR"
	// NotificationTypeSilentPush will just emit a phone notification
	NotificationTypeSilentPush NotificationType = "SILENT_PUSH"
	// NotificationTypeNoPush will not emit sound/vibration nor a phone notification
	NotificationTypeNoPush NotificationType = "NO_PUSH"
)

type MessageQuery struct {
	Recipient        Recipient        `json:"recipient"`
	Message          SendMessage      `json:"message"`
	NotificationType NotificationType `json:"notification_type,omitempty"`
}

func (mq *MessageQuery) RecipientID(recipientID string) error {
	if mq.Recipient.PhoneNumber != "" {
		return errors.New("Only one user identification (phone or id) can be specified.")
	}
	mq.Recipient.ID = recipientID
	return nil
}

func (mq *MessageQuery) RecipientPhoneNumber(phoneNumber string, button template.Button) error {
	if mq.Recipient.ID != "" {
		return errors.New("Only one user identification (phone or id) can be specified.")
	}
	mq.Recipient.PhoneNumber = phoneNumber
	return nil
}

func (mq *MessageQuery) Notification(notification NotificationType) *MessageQuery {
	mq.NotificationType = notification
	return mq
}

func (mq *MessageQuery) Text(text string) error {
	if mq.Message.Attachment == nil {
		mq.Message.Attachment = &Attachment{}
	}
	if mq.Message.Attachment != nil && mq.Message.Attachment.Type == AttachmentTypeTemplate {
		return errors.New("Can't set both text and template.")
	}
	mq.Message.Text = text;
	return nil
}

func (mq *MessageQuery) resource(typ AttachmentType, url string) error {
	if mq.Message.Attachment == nil {
		mq.Message.Attachment = &Attachment{}
	}
	if mq.Message.Attachment.Payload != nil {
		return errors.New("Attachment already specified.")
	}
	mq.Message.Attachment.Type = typ
	mq.Message.Attachment.Payload = &Resource{URL:url}
	return nil
}

func (mq *MessageQuery) Audio(url string) error {
	return mq.resource(AttachmentTypeAudio, url)
}

func (mq *MessageQuery) Video(url string) error {
	return mq.resource(AttachmentTypeVideo, url)
}

func (mq *MessageQuery) Image(url string) error {
	return mq.resource(AttachmentTypeImage, url)
}

func (mq *MessageQuery) Template(tpl template.Template) error {
	if mq.Message.Attachment == nil {
		mq.Message.Attachment = &Attachment{}
	}
	if mq.Message.Attachment.Type != AttachmentTypeTemplate && mq.Message.Attachment.Payload != nil {
		return errors.New("Non-template attachment already specified.")
	}

	if mq.Message.Attachment.Payload == nil {
		mq.Message.Attachment.Type = AttachmentTypeTemplate
		mq.Message.Attachment.Payload = &template.Payload{}
	}

	payload := mq.Message.Attachment.Payload.(*template.Payload)

	for _,v := range payload.Elements {
		if v.Type() != tpl.Type() {
			return errors.New("All templates have to have thesame type.")
		}
	}

	payload.Elements = append(payload.Elements, tpl)
	return nil
}

<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 testfiles provides a wrapper around various optional ways
// of retrieving additional files needed during a test run:
// - builtin bindata
// - filesystem access
//
// Because it is a is self-contained package, it can be used by
// test/e2e/framework and test/e2e/manifest without creating
// a circular dependency.
package testfiles

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
)

var filesources []FileSource

// AddFileSource registers another provider for files that may be
// needed at runtime. Should be called during initialization of a test
// binary.
func AddFileSource(filesource FileSource) {
	filesources = append(filesources, filesource)
}

// FileSource implements one way of retrieving test file content.  For
// example, one file source could read from the original source code
// file tree, another from bindata compiled into a test executable.
type FileSource interface {
	// ReadTestFile retrieves the content of a file that gets maintained
	// alongside a test's source code. Files are identified by the
	// relative path inside the repository containing the tests, for
	// example "cluster/gce/upgrade.sh" inside kubernetes/kubernetes.
	//
	// When the file is not found, a nil slice is returned. An error is
	// returned for all fatal errors.
	ReadTestFile(filePath string) ([]byte, error)

	// DescribeFiles returns a multi-line description of which
	// files are available via this source. It is meant to be
	// used as part of the error message when a file cannot be
	// found.
	DescribeFiles() string
}

// Fail is an error handler function with the same prototype and
// semantic as ginkgo.Fail. Typically ginkgo.Fail is what callers
// of ReadOrDie and Exists will pass. This way this package
// avoids depending on Ginkgo.
type Fail func(failure string, callerSkip ...int)

// ReadOrDie tries to retrieve the desired file content from
// one of the registered file sources. In contrast to FileSource, it
// will either return a valid slice or abort the test by calling the fatal function,
// i.e. the caller doesn't have to implement error checking.
func ReadOrDie(filePath string, fail Fail) []byte {
	data, err := Read(filePath)
	if err != nil {
		fail(err.Error(), 1)
	}
	return data
}

// Read tries to retrieve the desired file content from
// one of the registered file sources.
func Read(filePath string) ([]byte, error) {
	if len(filesources) == 0 {
		return nil, fmt.Errorf("no file sources registered (yet?), cannot retrieve test file %s", filePath)
	}
	for _, filesource := range filesources {
		data, err := filesource.ReadTestFile(filePath)
		if err != nil {
			return nil, fmt.Errorf("fatal error retrieving test file %s: %s", filePath, err)
		}
		if data != nil {
			return data, nil
		}
	}
	// Here we try to generate an error that points test authors
	// or users in the right direction for resolving the problem.
	error := fmt.Sprintf("Test file %q was not found.\n", filePath)
	for _, filesource := range filesources {
		error += filesource.DescribeFiles()
		error += "\n"
	}
	return nil, errors.New(error)
}

// Exists checks whether a file could be read. Unexpected errors
// are handled by calling the fail function, which then should
// abort the current test.
func Exists(filePath string, fail Fail) bool {
	for _, filesource := range filesources {
		data, err := filesource.ReadTestFile(filePath)
		if err != nil {
			fail(fmt.Sprintf("fatal error looking for test file %s: %s", filePath, err), 1)
		}
		if data != nil {
			return true
		}
	}
	return false
}

// RootFileSource looks for files relative to a root directory.
type RootFileSource struct {
	Root string
}

// ReadTestFile looks for the file relative to the configured
// root directory.
func (r RootFileSource) ReadTestFile(filePath string) ([]byte, error) {
	fullPath := filepath.Join(r.Root, filePath)
	data, err := ioutil.ReadFile(fullPath)
	if os.IsNotExist(err) {
		// Not an error (yet), some other provider may have the file.
		return nil, nil
	}
	return data, err
}

// DescribeFiles explains that it looks for files inside a certain
// root directory.
func (r RootFileSource) DescribeFiles() string {
	description := fmt.Sprintf("Test files are expected in %q", r.Root)
	if !path.IsAbs(r.Root) {
		// The default in test_context.go is the relative path
		// ../../, which doesn't really help locating the
		// actual location. Therefore we add also the absolute
		// path if necessary.
		abs, err := filepath.Abs(r.Root)
		if err == nil {
			description += fmt.Sprintf(" = %q", abs)
		}
	}
	description += "."
	return description
}

// BindataFileSource handles files stored in a package generated with bindata.
type BindataFileSource struct {
	Asset      func(string) ([]byte, error)
	AssetNames func() []string
}

// ReadTestFile looks for an asset with the given path.
func (b BindataFileSource) ReadTestFile(filePath string) ([]byte, error) {
	fileBytes, err := b.Asset(filePath)
	if err != nil {
		// It would be nice to have a better way to detect
		// "not found" errors :-/
		if strings.HasSuffix(err.Error(), "not found") {
			return nil, nil
		}
	}
	return fileBytes, nil
}

// DescribeFiles explains about gobindata and then lists all available files.
func (b BindataFileSource) DescribeFiles() string {
	var lines []string
	lines = append(lines, "The following files are built into the test executable via gobindata. For questions on maintaining gobindata, contact the sig-testing group.")
	assets := b.AssetNames()
	sort.Strings(assets)
	lines = append(lines, assets...)
	description := strings.Join(lines, "\n   ")
	return description
}
<commit_msg>e2e/framework: absolute test file paths<commit_after>/*
Copyright 2018 The Kubernetes 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 testfiles provides a wrapper around various optional ways
// of retrieving additional files needed during a test run:
// - builtin bindata
// - filesystem access
//
// Because it is a is self-contained package, it can be used by
// test/e2e/framework and test/e2e/manifest without creating
// a circular dependency.
package testfiles

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
)

var filesources []FileSource

// AddFileSource registers another provider for files that may be
// needed at runtime. Should be called during initialization of a test
// binary.
func AddFileSource(filesource FileSource) {
	filesources = append(filesources, filesource)
}

// FileSource implements one way of retrieving test file content.  For
// example, one file source could read from the original source code
// file tree, another from bindata compiled into a test executable.
type FileSource interface {
	// ReadTestFile retrieves the content of a file that gets maintained
	// alongside a test's source code. Files are identified by the
	// relative path inside the repository containing the tests, for
	// example "cluster/gce/upgrade.sh" inside kubernetes/kubernetes.
	//
	// When the file is not found, a nil slice is returned. An error is
	// returned for all fatal errors.
	ReadTestFile(filePath string) ([]byte, error)

	// DescribeFiles returns a multi-line description of which
	// files are available via this source. It is meant to be
	// used as part of the error message when a file cannot be
	// found.
	DescribeFiles() string
}

// Fail is an error handler function with the same prototype and
// semantic as ginkgo.Fail. Typically ginkgo.Fail is what callers
// of ReadOrDie and Exists will pass. This way this package
// avoids depending on Ginkgo.
type Fail func(failure string, callerSkip ...int)

// ReadOrDie tries to retrieve the desired file content from
// one of the registered file sources. In contrast to FileSource, it
// will either return a valid slice or abort the test by calling the fatal function,
// i.e. the caller doesn't have to implement error checking.
func ReadOrDie(filePath string, fail Fail) []byte {
	data, err := Read(filePath)
	if err != nil {
		fail(err.Error(), 1)
	}
	return data
}

// Read tries to retrieve the desired file content from
// one of the registered file sources.
func Read(filePath string) ([]byte, error) {
	if len(filesources) == 0 {
		return nil, fmt.Errorf("no file sources registered (yet?), cannot retrieve test file %s", filePath)
	}
	for _, filesource := range filesources {
		data, err := filesource.ReadTestFile(filePath)
		if err != nil {
			return nil, fmt.Errorf("fatal error retrieving test file %s: %s", filePath, err)
		}
		if data != nil {
			return data, nil
		}
	}
	// Here we try to generate an error that points test authors
	// or users in the right direction for resolving the problem.
	error := fmt.Sprintf("Test file %q was not found.\n", filePath)
	for _, filesource := range filesources {
		error += filesource.DescribeFiles()
		error += "\n"
	}
	return nil, errors.New(error)
}

// Exists checks whether a file could be read. Unexpected errors
// are handled by calling the fail function, which then should
// abort the current test.
func Exists(filePath string, fail Fail) bool {
	for _, filesource := range filesources {
		data, err := filesource.ReadTestFile(filePath)
		if err != nil {
			fail(fmt.Sprintf("fatal error looking for test file %s: %s", filePath, err), 1)
		}
		if data != nil {
			return true
		}
	}
	return false
}

// RootFileSource looks for files relative to a root directory.
type RootFileSource struct {
	Root string
}

// ReadTestFile looks for the file relative to the configured
// root directory. If the path is already absolute, for example
// in a test that has its own method of determining where
// files are, then the path will be used directly.
func (r RootFileSource) ReadTestFile(filePath string) ([]byte, error) {
	var fullPath string
	if path.IsAbs(filePath) {
		fullPath = filePath
	} else {
		fullPath = filepath.Join(r.Root, filePath)
	}
	data, err := ioutil.ReadFile(fullPath)
	if os.IsNotExist(err) {
		// Not an error (yet), some other provider may have the file.
		return nil, nil
	}
	return data, err
}

// DescribeFiles explains that it looks for files inside a certain
// root directory.
func (r RootFileSource) DescribeFiles() string {
	description := fmt.Sprintf("Test files are expected in %q", r.Root)
	if !path.IsAbs(r.Root) {
		// The default in test_context.go is the relative path
		// ../../, which doesn't really help locating the
		// actual location. Therefore we add also the absolute
		// path if necessary.
		abs, err := filepath.Abs(r.Root)
		if err == nil {
			description += fmt.Sprintf(" = %q", abs)
		}
	}
	description += "."
	return description
}

// BindataFileSource handles files stored in a package generated with bindata.
type BindataFileSource struct {
	Asset      func(string) ([]byte, error)
	AssetNames func() []string
}

// ReadTestFile looks for an asset with the given path.
func (b BindataFileSource) ReadTestFile(filePath string) ([]byte, error) {
	fileBytes, err := b.Asset(filePath)
	if err != nil {
		// It would be nice to have a better way to detect
		// "not found" errors :-/
		if strings.HasSuffix(err.Error(), "not found") {
			return nil, nil
		}
	}
	return fileBytes, nil
}

// DescribeFiles explains about gobindata and then lists all available files.
func (b BindataFileSource) DescribeFiles() string {
	var lines []string
	lines = append(lines, "The following files are built into the test executable via gobindata. For questions on maintaining gobindata, contact the sig-testing group.")
	assets := b.AssetNames()
	sort.Strings(assets)
	lines = append(lines, assets...)
	description := strings.Join(lines, "\n   ")
	return description
}
<|endoftext|>


<commit_before>package adhandler

import (
	"encoding/json"
	"net/http"
	"strconv"
	"time"
	"math/rand"
	"adserver"
	"strings"
	"fmt"
	"bytes"
	"encoding/base64"
	"github.com/satori/go.uuid"
)

func SearchHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	req := new(adserver.Request)
	// slot_id
	if len(r.Form["slot_id"]) > 0 {
		slotId, _ := strconv.ParseUint(r.Form["slot_id"][0], 10, 32)
		req.SlotId = uint32(slotId)
	}
	// ad_num
	if len(r.Form["ad_num"]) > 0 {
		adNum, _ := strconv.ParseUint(r.Form["ad_num"][0], 10, 32)
		req.AdNum = uint32(adNum)
	}
	// ip
	if len(r.Form["ip"]) > 0 {
		req.Ip = r.Form["ip"][0]
	}
	// device_id
	if len(r.Form["device_id"]) > 0 {
		req.DeviceId = r.Form["device_id"][0]
	}
	// os
	if len(r.Form["os"]) > 0 {
		os, _ := strconv.ParseUint(r.Form["os"][0], 10, 32)
		req.Os = uint32(os)
	}
	// os_version
	if len(r.Form["os_version"]) > 0 {
		req.OsVersion = r.Form["os_version"][0]
	}

	// searchId
	req.SearchId = uuid.NewV4().String()

	adData := adserver.AdDictObject.GetCurrentAdData()
	// search by request ip
	var unitIdList1 []uint32
	var exist1 bool
	locationInfo := adserver.SearchLocationByIp(req.Ip)
	if locationInfo != nil {
		country := locationInfo.Country
		city := locationInfo.City
		adserver.AdServerLog.Debug(fmt.Sprintf(
			"ip=%s country=%s city=%s\n", req.Ip, country, city))
		key := strings.ToLower(country) + "_" + strings.ToLower(city)
		unitIdList1, exist1 = adData.LocationUnitMap[key]
	}
	// search by CN_ALL
	key := "cn_all"
	unitIdList2, exist2 := adData.LocationUnitMap[key]
	// merge two unit id list
	unitNum := 0
	if exist1 {
		unitNum += len(unitIdList1)
	}
	if exist2 {
		unitNum += len(unitIdList2)
	}
	unitIdList := make([]uint32, unitNum)
	if exist1 && unitIdList1 != nil {
		copy(unitIdList, unitIdList1)
	}
	if exist2 && unitIdList2 != nil {
		copy(unitIdList, unitIdList2)
	}

	// select one from unit id list
	var res = &adserver.Response{}
	if unitIdList != nil {
		unitNum = len(unitIdList)
		random := rand.New(rand.NewSource(time.Now().UnixNano()))
		randIndex := random.Intn(unitNum)
		unitId := unitIdList[randIndex]
		unitInfo := adData.AdUnitMap[unitId]
		adCreative := adData.AdCreativeMap[unitInfo.CreativeId]

		adInfo := adserver.AdInfo{
			UnitId: unitInfo.UnitId,
			CreativeId: adCreative.CreativeId,
			Title: adCreative.Title,
			Description: adCreative.Description,
			AppPackageName: adCreative.AppPackageName,
			IconImageUrl: adCreative.IconImageUrl,
			MainImageUrl: adCreative.MainImageUrl,
			ClickUrl: adCreative.ClickUrl,
		}
		adInfo.ImpressionTrackUrl = buildImpressionTrackUrl(req, adInfo)
		adInfo.ClickTrackUrl = buildClickTrackUrl(req, adInfo)
		adInfo.ConversionTrackUrl = buildConversionTrackUrl(req, adInfo)
		adList := make([]adserver.AdInfo, 0, 1)
		adList = append(adList, adInfo)
		res.ResCode = 0
		res.AdList = adList
		adserver.SearchLog.Info(fmt.Sprintf(
			"searchId=%s slotId=%d adNum=%d iP=%s deviceId=%s oS=%d osVersion=%s " +
			"unitId=%d creativeId=%d\n",
			req.SearchId, req.SlotId, req.AdNum, req.Ip, req.DeviceId, req.Os, req.OsVersion,
			adInfo.UnitId, adInfo.CreativeId))
	} else {
		res.ResCode = 0
		res.AdList = make([]adserver.AdInfo, 0, 1)
		adserver.SearchLog.Info(fmt.Sprintf(
			"searchId=%s slotId=%d adNum=%d iP=%s deviceId=%s oS=%d osVersion=%s resNum=0\n",
			req.SearchId, req.SlotId, req.AdNum, req.Ip, req.DeviceId, req.Os, req.OsVersion))
	}

	resBytes, _ := json.Marshal(res)
	w.Write(resBytes)
}

func buildImpressionTrackUrl(req *adserver.Request, adInfo adserver.AdInfo) string {
	var paramBuf bytes.Buffer
	paramBuf.WriteString(fmt.Sprintf("search_id=%s", req.SearchId))
	paramBuf.WriteString(fmt.Sprintf("&slot_id=%d", req.SlotId))
	paramBuf.WriteString(fmt.Sprintf("&ip=%s", req.Ip))
	paramBuf.WriteString(fmt.Sprintf("&device_id=%s", req.DeviceId))
	paramBuf.WriteString(fmt.Sprintf("&os=%d", req.Os))
	paramBuf.WriteString(fmt.Sprintf("&os_version=%s", req.OsVersion))
	paramBuf.WriteString(fmt.Sprintf("&unit_id=%d", adInfo.UnitId))
	paramBuf.WriteString(fmt.Sprintf("&creative_id=%d", adInfo.CreativeId))
	paramEncoded := base64.StdEncoding.EncodeToString(paramBuf.Bytes())
	impressionTrackUrl := fmt.Sprintf("%s?i=%s",
		adserver.GlobalConfObject.ImpressionTrackUrlPrefix, paramEncoded)
	return impressionTrackUrl
}

func buildClickTrackUrl(req *adserver.Request, adInfo adserver.AdInfo) string {
	var paramBuf bytes.Buffer
	paramBuf.WriteString(fmt.Sprintf("search_id=%s", req.SearchId))
	paramBuf.WriteString(fmt.Sprintf("&slot_id=%d", req.SlotId))
	paramBuf.WriteString(fmt.Sprintf("&ip=%s", req.Ip))
	paramBuf.WriteString(fmt.Sprintf("&device_id=%s", req.DeviceId))
	paramBuf.WriteString(fmt.Sprintf("&os=%d", req.Os))
	paramBuf.WriteString(fmt.Sprintf("&os_version=%s", req.OsVersion))
	paramBuf.WriteString(fmt.Sprintf("&unit_id=%d", adInfo.UnitId))
	paramBuf.WriteString(fmt.Sprintf("&creative_id=%d", adInfo.CreativeId))
	paramBuf.WriteString(fmt.Sprintf("&click_url=%s", adInfo.ClickUrl))
	paramEncoded := base64.StdEncoding.EncodeToString(paramBuf.Bytes())
	impressionTrackUrl := fmt.Sprintf("%s?i=%s",
		adserver.GlobalConfObject.ClickTrackUrlPrefix, paramEncoded)
	return impressionTrackUrl
}

func buildConversionTrackUrl(req *adserver.Request, adInfo adserver.AdInfo) string {
	var paramBuf bytes.Buffer
	paramBuf.WriteString(fmt.Sprintf("search_id=%s", req.SearchId))
	paramBuf.WriteString(fmt.Sprintf("&slot_id=%d", req.SlotId))
	paramBuf.WriteString(fmt.Sprintf("&ip=%s", req.Ip))
	paramBuf.WriteString(fmt.Sprintf("&device_id=%s", req.DeviceId))
	paramBuf.WriteString(fmt.Sprintf("&os=%d", req.Os))
	paramBuf.WriteString(fmt.Sprintf("&os_version=%s", req.OsVersion))
	paramBuf.WriteString(fmt.Sprintf("&unit_id=%d", adInfo.UnitId))
	paramBuf.WriteString(fmt.Sprintf("&creative_id=%d", adInfo.CreativeId))
	paramBuf.WriteString(fmt.Sprintf("&click_url=%s", adInfo.ClickUrl))
	paramEncoded := base64.StdEncoding.EncodeToString(paramBuf.Bytes())
	conversionTrackUrl := fmt.Sprintf("%s?i=%s",
		adserver.GlobalConfObject.ConversionTrackUrlPrefix, paramEncoded)
	return conversionTrackUrl
}

<commit_msg>add the function of returning multiple adinfo<commit_after>package adhandler

import (
	"encoding/json"
	"net/http"
	"strconv"
	"time"
	"math/rand"
	"adserver"
	"strings"
	"fmt"
	"bytes"
	"encoding/base64"
	"github.com/satori/go.uuid"
)

func SearchHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	req := new(adserver.Request)
	// slot_id
	if len(r.Form["slot_id"]) > 0 {
		slotId, _ := strconv.ParseUint(r.Form["slot_id"][0], 10, 32)
		req.SlotId = uint32(slotId)
	}
	// ad_num
	var reqAdNum int
	if len(r.Form["ad_num"]) > 0 {
		adNum, _ := strconv.ParseUint(r.Form["ad_num"][0], 10, 32)
		reqAdNum, _ = strconv.Atoi(r.Form["ad_num"][0])
		req.AdNum = uint32(adNum)
	}
	// ip
	if len(r.Form["ip"]) > 0 {
		req.Ip = r.Form["ip"][0]
	}
	// device_id
	if len(r.Form["device_id"]) > 0 {
		req.DeviceId = r.Form["device_id"][0]
	}
	// os
	if len(r.Form["os"]) > 0 {
		os, _ := strconv.ParseUint(r.Form["os"][0], 10, 32)
		req.Os = uint32(os)
	}
	// os_version
	if len(r.Form["os_version"]) > 0 {
		req.OsVersion = r.Form["os_version"][0]
	}

	// searchId
	req.SearchId = uuid.NewV4().String()

	adData := adserver.AdDictObject.GetCurrentAdData()
	// search by request ip
	var unitIdList1 []uint32
	var exist1 bool
	locationInfo := adserver.SearchLocationByIp(req.Ip)
	if locationInfo != nil {
		country := locationInfo.Country
		city := locationInfo.City
		adserver.AdServerLog.Debug(fmt.Sprintf(
			"ip=%s country=%s city=%s\n", req.Ip, country, city))
		key := strings.ToLower(country) + "_" + strings.ToLower(city)
		unitIdList1, exist1 = adData.LocationUnitMap[key]
	}
	// search by CN_ALL
	key := "cn_all"
	unitIdList2, exist2 := adData.LocationUnitMap[key]
	// merge two unit id list
	unitNum := 0
	if exist1 {
		unitNum += len(unitIdList1)
	}
	if exist2 {
		unitNum += len(unitIdList2)
	}
	unitIdList := make([]uint32, unitNum)
	if exist1 && unitIdList1 != nil {
		copy(unitIdList, unitIdList1)
	}
	if exist2 && unitIdList2 != nil {
		copy(unitIdList, unitIdList2)
	}

	// select one from unit id list
	var res = &adserver.Response{}
	adList := make([]adserver.AdInfo, 0, 1)
	unitIdMap := make(map[int] bool)
	var unitIdsStr, creativeIdsStr string
	if unitIdList != nil && reqAdNum >= 1 {
		unitNum = len(unitIdList)
		random := rand.New(rand.NewSource(time.Now().UnixNano()))
		for i := 0; i < unitNum && i < reqAdNum; i++ {
			randIndex := random.Intn(unitNum)
			if unitIdMap[randIndex] {
				i--
				continue
			}
			unitIdMap[randIndex] = true
			unitId := unitIdList[randIndex]
			unitInfo := adData.AdUnitMap[unitId]
			adCreative := adData.AdCreativeMap[unitInfo.CreativeId]
			adInfo := adserver.AdInfo{
				UnitId: unitInfo.UnitId,
				CreativeId: adCreative.CreativeId,
				Title: adCreative.Title,
				Description: adCreative.Description,
				AppPackageName: adCreative.AppPackageName,
				IconImageUrl: adCreative.IconImageUrl,
				MainImageUrl: adCreative.MainImageUrl,
				ClickUrl: adCreative.ClickUrl,
			}
			adInfo.ImpressionTrackUrl = buildImpressionTrackUrl(req, adInfo)
			adInfo.ClickTrackUrl = buildClickTrackUrl(req, adInfo)
			adInfo.ConversionTrackUrl = buildConversionTrackUrl(req, adInfo)
			adList = append(adList, adInfo)
			if i == unitNum - 1 || i == reqAdNum - 1 {
				unitIdsStr += fmt.Sprint(adInfo.UnitId)
				creativeIdsStr += fmt.Sprint(adInfo.CreativeId)
			} else {
				unitIdsStr += fmt.Sprint(adInfo.UnitId) + ","
				creativeIdsStr += fmt.Sprint(adInfo.CreativeId) + ","
			}
		}
		res.ResCode = 0
		res.AdList = adList
	} else {
		res.ResCode = 0
		res.AdList = make([]adserver.AdInfo, 0, 1)
	}
    adserver.SearchLog.Info(fmt.Sprintf(
			"searchId=%s slotId=%d adNum=%d iP=%s deviceId=%s oS=%d osVersion=%s " +
			"unitId=%s creativeId=%s\n",
			req.SearchId, req.SlotId, req.AdNum, req.Ip, req.DeviceId, req.Os, req.OsVersion,
			unitIdsStr, creativeIdsStr))

	resBytes, _ := json.Marshal(res)
	w.Write(resBytes)
}

func buildImpressionTrackUrl(req *adserver.Request, adInfo adserver.AdInfo) string {
	var paramBuf bytes.Buffer
	paramBuf.WriteString(fmt.Sprintf("search_id=%s", req.SearchId))
	paramBuf.WriteString(fmt.Sprintf("&slot_id=%d", req.SlotId))
	paramBuf.WriteString(fmt.Sprintf("&ip=%s", req.Ip))
	paramBuf.WriteString(fmt.Sprintf("&device_id=%s", req.DeviceId))
	paramBuf.WriteString(fmt.Sprintf("&os=%d", req.Os))
	paramBuf.WriteString(fmt.Sprintf("&os_version=%s", req.OsVersion))
	paramBuf.WriteString(fmt.Sprintf("&unit_id=%d", adInfo.UnitId))
	paramBuf.WriteString(fmt.Sprintf("&creative_id=%d", adInfo.CreativeId))
	paramEncoded := base64.StdEncoding.EncodeToString(paramBuf.Bytes())
	impressionTrackUrl := fmt.Sprintf("%s?i=%s",
		adserver.GlobalConfObject.ImpressionTrackUrlPrefix, paramEncoded)
	return impressionTrackUrl
}

func buildClickTrackUrl(req *adserver.Request, adInfo adserver.AdInfo) string {
	var paramBuf bytes.Buffer
	paramBuf.WriteString(fmt.Sprintf("search_id=%s", req.SearchId))
	paramBuf.WriteString(fmt.Sprintf("&slot_id=%d", req.SlotId))
	paramBuf.WriteString(fmt.Sprintf("&ip=%s", req.Ip))
	paramBuf.WriteString(fmt.Sprintf("&device_id=%s", req.DeviceId))
	paramBuf.WriteString(fmt.Sprintf("&os=%d", req.Os))
	paramBuf.WriteString(fmt.Sprintf("&os_version=%s", req.OsVersion))
	paramBuf.WriteString(fmt.Sprintf("&unit_id=%d", adInfo.UnitId))
	paramBuf.WriteString(fmt.Sprintf("&creative_id=%d", adInfo.CreativeId))
	paramBuf.WriteString(fmt.Sprintf("&click_url=%s", adInfo.ClickUrl))
	paramEncoded := base64.StdEncoding.EncodeToString(paramBuf.Bytes())
	impressionTrackUrl := fmt.Sprintf("%s?i=%s",
		adserver.GlobalConfObject.ClickTrackUrlPrefix, paramEncoded)
	return impressionTrackUrl
}

func buildConversionTrackUrl(req *adserver.Request, adInfo adserver.AdInfo) string {
	var paramBuf bytes.Buffer
	paramBuf.WriteString(fmt.Sprintf("search_id=%s", req.SearchId))
	paramBuf.WriteString(fmt.Sprintf("&slot_id=%d", req.SlotId))
	paramBuf.WriteString(fmt.Sprintf("&ip=%s", req.Ip))
	paramBuf.WriteString(fmt.Sprintf("&device_id=%s", req.DeviceId))
	paramBuf.WriteString(fmt.Sprintf("&os=%d", req.Os))
	paramBuf.WriteString(fmt.Sprintf("&os_version=%s", req.OsVersion))
	paramBuf.WriteString(fmt.Sprintf("&unit_id=%d", adInfo.UnitId))
	paramBuf.WriteString(fmt.Sprintf("&creative_id=%d", adInfo.CreativeId))
	paramBuf.WriteString(fmt.Sprintf("&click_url=%s", adInfo.ClickUrl))
	paramEncoded := base64.StdEncoding.EncodeToString(paramBuf.Bytes())
	conversionTrackUrl := fmt.Sprintf("%s?i=%s",
		adserver.GlobalConfObject.ConversionTrackUrlPrefix, paramEncoded)
	return conversionTrackUrl
}

<|endoftext|>


<commit_before>package api

import (
	"context"
	"sort"

	log "github.com/sirupsen/logrus"

	"github.com/bytom/account"
	"github.com/bytom/common"
	"github.com/bytom/consensus"
	"github.com/bytom/crypto/ed25519/chainkd"
	"github.com/bytom/protocol/vm/vmutil"
)

// POST /create-account
func (a *API) createAccount(ctx context.Context, ins struct {
	RootXPubs []chainkd.XPub `json:"root_xpubs"`
	Quorum    int            `json:"quorum"`
	Alias     string         `json:"alias"`
}) Response {
	acc, err := a.wallet.AccountMgr.Create(ins.RootXPubs, ins.Quorum, ins.Alias)
	if err != nil {
		return NewErrorResponse(err)
	}

	annotatedAccount := account.Annotated(acc)
	log.WithField("account ID", annotatedAccount.ID).Info("Created account")

	return NewSuccessResponse(annotatedAccount)
}

// AccountInfo is request struct for deleteAccount
type AccountInfo struct {
	Info string `json:"account_info"`
}

// POST /delete-account
func (a *API) deleteAccount(ctx context.Context, in AccountInfo) Response {
	if err := a.wallet.AccountMgr.DeleteAccount(in.Info); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(nil)
}

type validateAddressResp struct {
	Valid   bool `json:"valid"`
	IsLocal bool `json:"is_local"`
}

// POST /validate-address
func (a *API) validateAddress(ctx context.Context, ins struct {
	Address string `json:"address"`
}) Response {
	resp := &validateAddressResp{
		Valid:   false,
		IsLocal: false,
	}
	address, err := common.DecodeAddress(ins.Address, &consensus.ActiveNetParams)
	if err != nil {
		return NewSuccessResponse(resp)
	}

	redeemContract := address.ScriptAddress()
	program := []byte{}
	switch address.(type) {
	case *common.AddressWitnessPubKeyHash:
		program, err = vmutil.P2WPKHProgram(redeemContract)
	case *common.AddressWitnessScriptHash:
		program, err = vmutil.P2WSHProgram(redeemContract)
	default:
		return NewSuccessResponse(resp)
	}
	if err != nil {
		return NewSuccessResponse(resp)
	}

	resp.Valid = true
	resp.IsLocal = a.wallet.AccountMgr.IsLocalControlProgram(program)
	return NewSuccessResponse(resp)
}

type addressResp struct {
	AccountAlias string `json:"account_alias"`
	AccountID    string `json:"account_id"`
	Address      string `json:"address"`
	Change       bool   `json:"change"`
	KeyIndex     uint64 `json:"-"`
}

// SortByIndex implements sort.Interface for addressResp slices
type SortByIndex []addressResp

func (a SortByIndex) Len() int           { return len(a) }
func (a SortByIndex) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a SortByIndex) Less(i, j int) bool { return a[i].KeyIndex < a[j].KeyIndex }

func (a *API) listAddresses(ctx context.Context, ins struct {
	AccountID    string `json:"account_id"`
	AccountAlias string `json:"account_alias"`
	From         uint   `json:"from"`
	Count        uint   `json:"count"`
}) Response {
	accountID := ins.AccountID
	var target *account.Account
	if ins.AccountAlias != "" {
		acc, err := a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
		if err != nil {
			return NewErrorResponse(err)
		}
		target = acc
	} else {
		acc, err := a.wallet.AccountMgr.FindByID(accountID)
		if err != nil {
			return NewErrorResponse(err)
		}
		target = acc
	}

	cps, err := a.wallet.AccountMgr.ListControlProgram()
	if err != nil {
		return NewErrorResponse(err)
	}

	addresses := []addressResp{}
	for _, cp := range cps {
		if cp.Address == "" || cp.AccountID != target.ID {
			continue
		}
		addresses = append(addresses, addressResp{
			AccountAlias: target.Alias,
			AccountID:    cp.AccountID,
			Address:      cp.Address,
			Change:       cp.Change,
			KeyIndex:     cp.KeyIndex,
		})
	}

	// sort AddressResp by KeyIndex
	sort.Sort(SortByIndex(addresses))
	start, end := getPageRange(len(addresses), ins.From, ins.Count)
	return NewSuccessResponse(addresses[start:end])
}

type minigAddressResp struct {
	MiningAddress string `json:"mining_address"`
}

func (a *API) getMiningAddress(ctx context.Context) Response {
	miningAddress, err := a.wallet.AccountMgr.GetMiningAddress()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(minigAddressResp{
		MiningAddress: miningAddress,
	})
}

// POST /set-mining-address
func (a *API) setMiningAddress(ctx context.Context, in struct {
	MiningAddress string `json:"mining_address"`
}) Response {
	miningAddress, err := a.wallet.AccountMgr.SetMiningAddress(in.MiningAddress)
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(minigAddressResp{
		MiningAddress: miningAddress,
	})
}
<commit_msg>add control-program for list-addresses (#1235)<commit_after>package api

import (
	"context"
	"encoding/hex"
	"sort"

	log "github.com/sirupsen/logrus"

	"github.com/bytom/account"
	"github.com/bytom/common"
	"github.com/bytom/consensus"
	"github.com/bytom/crypto/ed25519/chainkd"
	"github.com/bytom/protocol/vm/vmutil"
)

// POST /create-account
func (a *API) createAccount(ctx context.Context, ins struct {
	RootXPubs []chainkd.XPub `json:"root_xpubs"`
	Quorum    int            `json:"quorum"`
	Alias     string         `json:"alias"`
}) Response {
	acc, err := a.wallet.AccountMgr.Create(ins.RootXPubs, ins.Quorum, ins.Alias)
	if err != nil {
		return NewErrorResponse(err)
	}

	annotatedAccount := account.Annotated(acc)
	log.WithField("account ID", annotatedAccount.ID).Info("Created account")

	return NewSuccessResponse(annotatedAccount)
}

// AccountInfo is request struct for deleteAccount
type AccountInfo struct {
	Info string `json:"account_info"`
}

// POST /delete-account
func (a *API) deleteAccount(ctx context.Context, in AccountInfo) Response {
	if err := a.wallet.AccountMgr.DeleteAccount(in.Info); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(nil)
}

type validateAddressResp struct {
	Valid   bool `json:"valid"`
	IsLocal bool `json:"is_local"`
}

// POST /validate-address
func (a *API) validateAddress(ctx context.Context, ins struct {
	Address string `json:"address"`
}) Response {
	resp := &validateAddressResp{
		Valid:   false,
		IsLocal: false,
	}
	address, err := common.DecodeAddress(ins.Address, &consensus.ActiveNetParams)
	if err != nil {
		return NewSuccessResponse(resp)
	}

	redeemContract := address.ScriptAddress()
	program := []byte{}
	switch address.(type) {
	case *common.AddressWitnessPubKeyHash:
		program, err = vmutil.P2WPKHProgram(redeemContract)
	case *common.AddressWitnessScriptHash:
		program, err = vmutil.P2WSHProgram(redeemContract)
	default:
		return NewSuccessResponse(resp)
	}
	if err != nil {
		return NewSuccessResponse(resp)
	}

	resp.Valid = true
	resp.IsLocal = a.wallet.AccountMgr.IsLocalControlProgram(program)
	return NewSuccessResponse(resp)
}

type addressResp struct {
	AccountAlias   string `json:"account_alias"`
	AccountID      string `json:"account_id"`
	Address        string `json:"address"`
	ControlProgram string `json:"control_program"`
	Change         bool   `json:"change"`
	KeyIndex       uint64 `json:"-"`
}

// SortByIndex implements sort.Interface for addressResp slices
type SortByIndex []addressResp

func (a SortByIndex) Len() int           { return len(a) }
func (a SortByIndex) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a SortByIndex) Less(i, j int) bool { return a[i].KeyIndex < a[j].KeyIndex }

func (a *API) listAddresses(ctx context.Context, ins struct {
	AccountID    string `json:"account_id"`
	AccountAlias string `json:"account_alias"`
	From         uint   `json:"from"`
	Count        uint   `json:"count"`
}) Response {
	accountID := ins.AccountID
	var target *account.Account
	if ins.AccountAlias != "" {
		acc, err := a.wallet.AccountMgr.FindByAlias(ins.AccountAlias)
		if err != nil {
			return NewErrorResponse(err)
		}
		target = acc
	} else {
		acc, err := a.wallet.AccountMgr.FindByID(accountID)
		if err != nil {
			return NewErrorResponse(err)
		}
		target = acc
	}

	cps, err := a.wallet.AccountMgr.ListControlProgram()
	if err != nil {
		return NewErrorResponse(err)
	}

	addresses := []addressResp{}
	for _, cp := range cps {
		if cp.Address == "" || cp.AccountID != target.ID {
			continue
		}
		addresses = append(addresses, addressResp{
			AccountAlias:   target.Alias,
			AccountID:      cp.AccountID,
			Address:        cp.Address,
			ControlProgram: hex.EncodeToString(cp.ControlProgram),
			Change:         cp.Change,
			KeyIndex:       cp.KeyIndex,
		})
	}

	// sort AddressResp by KeyIndex
	sort.Sort(SortByIndex(addresses))
	start, end := getPageRange(len(addresses), ins.From, ins.Count)
	return NewSuccessResponse(addresses[start:end])
}

type minigAddressResp struct {
	MiningAddress string `json:"mining_address"`
}

func (a *API) getMiningAddress(ctx context.Context) Response {
	miningAddress, err := a.wallet.AccountMgr.GetMiningAddress()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(minigAddressResp{
		MiningAddress: miningAddress,
	})
}

// POST /set-mining-address
func (a *API) setMiningAddress(ctx context.Context, in struct {
	MiningAddress string `json:"mining_address"`
}) Response {
	miningAddress, err := a.wallet.AccountMgr.SetMiningAddress(in.MiningAddress)
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(minigAddressResp{
		MiningAddress: miningAddress,
	})
}
<|endoftext|>


<commit_before>package driver

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/mail"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/blang/semver"
	"github.com/concourse/semver-resource/version"
)

var gitRepoDir string
var privateKeyPath string
var netRcPath string

var ErrEncryptedKey = errors.New("private keys with passphrases are not supported")

func init() {
	gitRepoDir = filepath.Join(os.TempDir(), "semver-git-repo")
	privateKeyPath = filepath.Join(os.TempDir(), "private-key")
	netRcPath = filepath.Join(os.Getenv("HOME"), ".netrc")
}

type GitDriver struct {
	InitialVersion semver.Version

	URI        string
	Branch     string
	PrivateKey string
	Username   string
	Password   string
	File       string
	GitUser    string
}

func (driver *GitDriver) Bump(bump version.Bump) (semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return semver.Version{}, err
	}

	err = driver.setUserInfo()
	if err != nil {
		return semver.Version{}, err
	}

	var newVersion semver.Version

	for {
		err = driver.setUpRepo()
		if err != nil {
			return semver.Version{}, err
		}

		currentVersion, exists, err := driver.readVersion()
		if err != nil {
			return semver.Version{}, err
		}

		if !exists {
			currentVersion = driver.InitialVersion
		}

		newVersion = bump.Apply(currentVersion)

		wrote, err := driver.writeVersion(newVersion)
		if wrote {
			break
		}
	}

	return newVersion, nil
}

func (driver *GitDriver) Set(newVersion semver.Version) error {
	err := driver.setUpAuth()
	if err != nil {
		return err
	}

	err = driver.setUserInfo()
	if err != nil {
		return err
	}

	for {
		err = driver.setUpRepo()
		if err != nil {
			return err
		}

		wrote, err := driver.writeVersion(newVersion)
		if err != nil {
			return err
		}

		if wrote {
			break
		}
	}

	return nil
}

func (driver *GitDriver) Check(cursor *semver.Version) ([]semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return nil, err
	}

	err = driver.setUpRepo()
	if err != nil {
		return nil, err
	}

	currentVersion, exists, err := driver.readVersion()
	if err != nil {
		return nil, err
	}

	if !exists {
		return []semver.Version{driver.InitialVersion}, nil
	}

	if cursor == nil || currentVersion.GTE(*cursor) {
		return []semver.Version{currentVersion}, nil
	}

	return []semver.Version{}, nil
}

func (driver *GitDriver) setUpRepo() error {
	_, err := os.Stat(gitRepoDir)
	if err != nil {
		gitClone := exec.Command("git", "clone", driver.URI, "--branch", driver.Branch, gitRepoDir)
		gitClone.Stdout = os.Stderr
		gitClone.Stderr = os.Stderr
		if err := gitClone.Run(); err != nil {
			return err
		}
	} else {
		gitFetch := exec.Command("git", "fetch", "origin", driver.Branch)
		gitFetch.Dir = gitRepoDir
		gitFetch.Stdout = os.Stderr
		gitFetch.Stderr = os.Stderr
		if err := gitFetch.Run(); err != nil {
			return err
		}
	}

	gitCheckout := exec.Command("git", "reset", "--hard", "origin/"+driver.Branch)
	gitCheckout.Dir = gitRepoDir
	gitCheckout.Stdout = os.Stderr
	gitCheckout.Stderr = os.Stderr
	if err := gitCheckout.Run(); err != nil {
		return err
	}

	return nil
}

func (driver *GitDriver) setUpAuth() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	} else {
		err := os.Remove(netRcPath)
		if err != nil {
			return err
		}
	}

	if len(driver.PrivateKey) > 0 {
		err := driver.setUpKey()
		if err != nil {
			return err
		}
	}

	if len(driver.Username) > 0 && len(driver.Password) > 0 {
		err := driver.setUpUsernamePassword()
		if err != nil {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUpKey() error {
	if strings.Contains(driver.PrivateKey, "ENCRYPTED") {
		return ErrEncryptedKey
	}

	_, err := os.Stat(privateKeyPath)
	if err != nil {
		if os.IsNotExist(err) {
			err := ioutil.WriteFile(privateKeyPath, []byte(driver.PrivateKey), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -i "+privateKeyPath)
}

func (driver *GitDriver) setUpUsernamePassword() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if os.IsNotExist(err) {
			content := fmt.Sprintf("default login %s password %s", driver.Username, driver.Password)
			err := ioutil.WriteFile(netRcPath, []byte(content), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUserInfo() error {
	if len(driver.GitUser) == 0 {
		return nil
	}

	e, err := mail.ParseAddress(driver.GitUser)
	if err != nil {
		return err
	}

	if len(e.Name) > 0 {
		gitName := exec.Command("git", "config", "--global", "user.name", e.Name)
		gitName.Stdout = os.Stderr
		gitName.Stderr = os.Stderr
		if err := gitName.Run(); err != nil {
			return err
		}
	}

	gitEmail := exec.Command("git", "config", "--global", "user.email", e.Address)
	gitEmail.Stdout = os.Stderr
	gitEmail.Stderr = os.Stderr
	if err := gitEmail.Run(); err != nil {
		return err
	}
	return nil
}

func (driver *GitDriver) readVersion() (semver.Version, bool, error) {
	var currentVersionStr string
	versionFile, err := os.Open(filepath.Join(gitRepoDir, driver.File))
	if err != nil {
		if os.IsNotExist(err) {
			return semver.Version{}, false, nil
		}

		return semver.Version{}, false, err
	}

	defer versionFile.Close()

	_, err = fmt.Fscanf(versionFile, "%s", &currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	currentVersion, err := semver.Parse(currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	return currentVersion, true, nil
}

const nothingToCommitString = "nothing to commit"
const falsePushString = "Everything up-to-date"
const pushRejectedString = "[rejected]"
const pushRemoteRejectedString = "[remote rejected]"

func (driver *GitDriver) writeVersion(newVersion semver.Version) (bool, error) {
	err := ioutil.WriteFile(filepath.Join(gitRepoDir, driver.File), []byte(newVersion.String()+"\n"), 0644)
	if err != nil {
		return false, err
	}

	gitAdd := exec.Command("git", "add", driver.File)
	gitAdd.Dir = gitRepoDir
	gitAdd.Stdout = os.Stderr
	gitAdd.Stderr = os.Stderr
	if err := gitAdd.Run(); err != nil {
		return false, err
	}

	gitCommit := exec.Command("git", "commit", "-m", "bump to "+newVersion.String())
	gitCommit.Dir = gitRepoDir

	commitOutput, err := gitCommit.CombinedOutput()

	if strings.Contains(string(commitOutput), nothingToCommitString) {
		return true, nil
	}

	if err != nil {
		os.Stderr.Write(commitOutput)
		return false, err
	}

	gitPush := exec.Command("git", "push", "origin", "HEAD:"+driver.Branch)
	gitPush.Dir = gitRepoDir

	pushOutput, err := gitPush.CombinedOutput()

	if strings.Contains(string(pushOutput), falsePushString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRejectedString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRemoteRejectedString) {
		return false, nil
	}

	if err != nil {
		os.Stderr.Write(pushOutput)
		return false, err
	}

	return true, nil
}
<commit_msg>Introduce --depth option for git driver<commit_after>package driver

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/mail"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/blang/semver"
	"github.com/concourse/semver-resource/version"
)

var gitRepoDir string
var privateKeyPath string
var netRcPath string

var ErrEncryptedKey = errors.New("private keys with passphrases are not supported")

func init() {
	gitRepoDir = filepath.Join(os.TempDir(), "semver-git-repo")
	privateKeyPath = filepath.Join(os.TempDir(), "private-key")
	netRcPath = filepath.Join(os.Getenv("HOME"), ".netrc")
}

type GitDriver struct {
	InitialVersion semver.Version

	URI        string
	Branch     string
	PrivateKey string
	Username   string
	Password   string
	File       string
	GitUser    string
	Depth      string
}

func (driver *GitDriver) Bump(bump version.Bump) (semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return semver.Version{}, err
	}

	err = driver.setUserInfo()
	if err != nil {
		return semver.Version{}, err
	}

	var newVersion semver.Version

	for {
		err = driver.setUpRepo()
		if err != nil {
			return semver.Version{}, err
		}

		currentVersion, exists, err := driver.readVersion()
		if err != nil {
			return semver.Version{}, err
		}

		if !exists {
			currentVersion = driver.InitialVersion
		}

		newVersion = bump.Apply(currentVersion)

		wrote, err := driver.writeVersion(newVersion)
		if wrote {
			break
		}
	}

	return newVersion, nil
}

func (driver *GitDriver) Set(newVersion semver.Version) error {
	err := driver.setUpAuth()
	if err != nil {
		return err
	}

	err = driver.setUserInfo()
	if err != nil {
		return err
	}

	for {
		err = driver.setUpRepo()
		if err != nil {
			return err
		}

		wrote, err := driver.writeVersion(newVersion)
		if err != nil {
			return err
		}

		if wrote {
			break
		}
	}

	return nil
}

func (driver *GitDriver) Check(cursor *semver.Version) ([]semver.Version, error) {
	err := driver.setUpAuth()
	if err != nil {
		return nil, err
	}

	err = driver.setUpRepo()
	if err != nil {
		return nil, err
	}

	currentVersion, exists, err := driver.readVersion()
	if err != nil {
		return nil, err
	}

	if !exists {
		return []semver.Version{driver.InitialVersion}, nil
	}

	if cursor == nil || currentVersion.GTE(*cursor) {
		return []semver.Version{currentVersion}, nil
	}

	return []semver.Version{}, nil
}

func (driver *GitDriver) setUpRepo() error {
	_, err := os.Stat(gitRepoDir)
	if err != nil {
		gitClone := exec.Command("git", "clone", driver.URI, "--branch", driver.Branch)
		if len(driver.Depth) > 0 {
			gitClone.Args = append(gitClone.Args, "--depth", driver.Depth)
		}
		gitClone.Args = append(gitClone.Args, gitRepoDir)
		gitClone.Stdout = os.Stderr
		gitClone.Stderr = os.Stderr
		if err := gitClone.Run(); err != nil {
			return err
		}
	} else {
		gitFetch := exec.Command("git", "fetch", "origin", driver.Branch)
		gitFetch.Dir = gitRepoDir
		gitFetch.Stdout = os.Stderr
		gitFetch.Stderr = os.Stderr
		if err := gitFetch.Run(); err != nil {
			return err
		}
	}

	gitCheckout := exec.Command("git", "reset", "--hard", "origin/"+driver.Branch)
	gitCheckout.Dir = gitRepoDir
	gitCheckout.Stdout = os.Stderr
	gitCheckout.Stderr = os.Stderr
	if err := gitCheckout.Run(); err != nil {
		return err
	}

	return nil
}

func (driver *GitDriver) setUpAuth() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	} else {
		err := os.Remove(netRcPath)
		if err != nil {
			return err
		}
	}

	if len(driver.PrivateKey) > 0 {
		err := driver.setUpKey()
		if err != nil {
			return err
		}
	}

	if len(driver.Username) > 0 && len(driver.Password) > 0 {
		err := driver.setUpUsernamePassword()
		if err != nil {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUpKey() error {
	if strings.Contains(driver.PrivateKey, "ENCRYPTED") {
		return ErrEncryptedKey
	}

	_, err := os.Stat(privateKeyPath)
	if err != nil {
		if os.IsNotExist(err) {
			err := ioutil.WriteFile(privateKeyPath, []byte(driver.PrivateKey), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return os.Setenv("GIT_SSH_COMMAND", "ssh -o StrictHostKeyChecking=no -i "+privateKeyPath)
}

func (driver *GitDriver) setUpUsernamePassword() error {
	_, err := os.Stat(netRcPath)
	if err != nil {
		if os.IsNotExist(err) {
			content := fmt.Sprintf("default login %s password %s", driver.Username, driver.Password)
			err := ioutil.WriteFile(netRcPath, []byte(content), 0600)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	return nil
}

func (driver *GitDriver) setUserInfo() error {
	if len(driver.GitUser) == 0 {
		return nil
	}

	e, err := mail.ParseAddress(driver.GitUser)
	if err != nil {
		return err
	}

	if len(e.Name) > 0 {
		gitName := exec.Command("git", "config", "--global", "user.name", e.Name)
		gitName.Stdout = os.Stderr
		gitName.Stderr = os.Stderr
		if err := gitName.Run(); err != nil {
			return err
		}
	}

	gitEmail := exec.Command("git", "config", "--global", "user.email", e.Address)
	gitEmail.Stdout = os.Stderr
	gitEmail.Stderr = os.Stderr
	if err := gitEmail.Run(); err != nil {
		return err
	}
	return nil
}

func (driver *GitDriver) readVersion() (semver.Version, bool, error) {
	var currentVersionStr string
	versionFile, err := os.Open(filepath.Join(gitRepoDir, driver.File))
	if err != nil {
		if os.IsNotExist(err) {
			return semver.Version{}, false, nil
		}

		return semver.Version{}, false, err
	}

	defer versionFile.Close()

	_, err = fmt.Fscanf(versionFile, "%s", &currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	currentVersion, err := semver.Parse(currentVersionStr)
	if err != nil {
		return semver.Version{}, false, err
	}

	return currentVersion, true, nil
}

const nothingToCommitString = "nothing to commit"
const falsePushString = "Everything up-to-date"
const pushRejectedString = "[rejected]"
const pushRemoteRejectedString = "[remote rejected]"

func (driver *GitDriver) writeVersion(newVersion semver.Version) (bool, error) {
	err := ioutil.WriteFile(filepath.Join(gitRepoDir, driver.File), []byte(newVersion.String()+"\n"), 0644)
	if err != nil {
		return false, err
	}

	gitAdd := exec.Command("git", "add", driver.File)
	gitAdd.Dir = gitRepoDir
	gitAdd.Stdout = os.Stderr
	gitAdd.Stderr = os.Stderr
	if err := gitAdd.Run(); err != nil {
		return false, err
	}

	gitCommit := exec.Command("git", "commit", "-m", "bump to "+newVersion.String())
	gitCommit.Dir = gitRepoDir

	commitOutput, err := gitCommit.CombinedOutput()

	if strings.Contains(string(commitOutput), nothingToCommitString) {
		return true, nil
	}

	if err != nil {
		os.Stderr.Write(commitOutput)
		return false, err
	}

	gitPush := exec.Command("git", "push", "origin", "HEAD:"+driver.Branch)
	gitPush.Dir = gitRepoDir

	pushOutput, err := gitPush.CombinedOutput()

	if strings.Contains(string(pushOutput), falsePushString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRejectedString) {
		return false, nil
	}

	if strings.Contains(string(pushOutput), pushRemoteRejectedString) {
		return false, nil
	}

	if err != nil {
		os.Stderr.Write(pushOutput)
		return false, err
	}

	return true, nil
}
<|endoftext|>


<commit_before>// +build linux

// Forked from https://github.com/containerd/containerd/blob/9ade247b38b5a685244e1391c86ff41ab109556e/snapshots/overlay/check.go
/*
   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 overlayutils

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/sys"
	"github.com/sirupsen/logrus"
)

// NeedsUserXAttr returns whether overlayfs should be mounted with the "userxattr" mount option.
//
// The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11.
//
// The "userxattr" option is NOT needed for the initial user namespace (aka "the host").
//
// Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount
// the overlayfs in a user namespace without the "userxattr" option.
//
// The corresponding kernel commit: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
// > ovl: user xattr
// >
// > Optionally allow using "user.overlay." namespace instead of "trusted.overlay."
// > ...
// > Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the
// > "user.overlay.redirect" or "user.overlay.metacopy" xattrs.
// > ...
//
// The "userxattr" support is not exposed in "/sys/module/overlay/parameters".
func NeedsUserXAttr(d string) (bool, error) {
	if !sys.RunningInUserNS() {
		// we are the real root (i.e., the root in the initial user NS),
		// so we do never need "userxattr" opt.
		return false, nil
	}

	// TODO: add fast path for kernel >= 5.11 .
	//
	// Keep in mind that distro vendors might be going to backport the patch to older kernels.
	// So we can't completely remove the check.

	tdRoot := filepath.Join(d, "userxattr-check")
	if err := os.RemoveAll(tdRoot); err != nil {
		logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
	}

	if err := os.MkdirAll(tdRoot, 0700); err != nil {
		return false, err
	}

	defer func() {
		if err := os.RemoveAll(tdRoot); err != nil {
			logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
		}
	}()

	td, err := ioutil.TempDir(tdRoot, "")
	if err != nil {
		return false, err
	}

	for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
		if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
			return false, err
		}
	}

	opts := []string{
		fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"), filepath.Join(td, "upper"), filepath.Join(td, "work")),
		"userxattr",
	}

	m := mount.Mount{
		Type:    "overlay",
		Source:  "overlay",
		Options: opts,
	}

	dest := filepath.Join(td, "merged")
	if err := m.Mount(dest); err != nil {
		// Probably the host is running Ubuntu/Debian kernel (< 5.11) with the userns patch but without the userxattr patch.
		// Return false without error.
		logrus.WithError(err).Debugf("cannot mount overlay with \"userxattr\", probably the kernel does not support userxattr")
		return false, nil
	}
	if err := mount.UnmountAll(dest, 0); err != nil {
		logrus.WithError(err).Warnf("Failed to unmount check directory %v", dest)
	}
	return true, nil
}
<commit_msg>overlayutils/userxattr.go: add "fast path" for kernel >= 5.11.0<commit_after>// +build linux

// Forked from https://github.com/containerd/containerd/blob/9ade247b38b5a685244e1391c86ff41ab109556e/snapshots/overlay/check.go
/*
   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 overlayutils

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/sys"
	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/sirupsen/logrus"
)

// NeedsUserXAttr returns whether overlayfs should be mounted with the "userxattr" mount option.
//
// The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11.
//
// The "userxattr" option is NOT needed for the initial user namespace (aka "the host").
//
// Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount
// the overlayfs in a user namespace without the "userxattr" option.
//
// The corresponding kernel commit: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
// > ovl: user xattr
// >
// > Optionally allow using "user.overlay." namespace instead of "trusted.overlay."
// > ...
// > Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the
// > "user.overlay.redirect" or "user.overlay.metacopy" xattrs.
// > ...
//
// The "userxattr" support is not exposed in "/sys/module/overlay/parameters".
func NeedsUserXAttr(d string) (bool, error) {
	if !sys.RunningInUserNS() {
		// we are the real root (i.e., the root in the initial user NS),
		// so we do never need "userxattr" opt.
		return false, nil
	}

	// Fast path for kernel >= 5.11 .
	//
	// Keep in mind that distro vendors might be going to backport the patch to older kernels.
	// So we can't completely remove the "slow path".
	if kernel.CheckKernelVersion(5, 11, 0) {
		return true, nil
	}

	tdRoot := filepath.Join(d, "userxattr-check")
	if err := os.RemoveAll(tdRoot); err != nil {
		logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
	}

	if err := os.MkdirAll(tdRoot, 0700); err != nil {
		return false, err
	}

	defer func() {
		if err := os.RemoveAll(tdRoot); err != nil {
			logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot)
		}
	}()

	td, err := ioutil.TempDir(tdRoot, "")
	if err != nil {
		return false, err
	}

	for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
		if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
			return false, err
		}
	}

	opts := []string{
		fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"), filepath.Join(td, "upper"), filepath.Join(td, "work")),
		"userxattr",
	}

	m := mount.Mount{
		Type:    "overlay",
		Source:  "overlay",
		Options: opts,
	}

	dest := filepath.Join(td, "merged")
	if err := m.Mount(dest); err != nil {
		// Probably the host is running Ubuntu/Debian kernel (< 5.11) with the userns patch but without the userxattr patch.
		// Return false without error.
		logrus.WithError(err).Debugf("cannot mount overlay with \"userxattr\", probably the kernel does not support userxattr")
		return false, nil
	}
	if err := mount.UnmountAll(dest, 0); err != nil {
		logrus.WithError(err).Warnf("Failed to unmount check directory %v", dest)
	}
	return true, nil
}
<|endoftext|>


<commit_before><commit_msg>fix(redhat): failed to parse date extra text: Z (#31)<commit_after><|endoftext|>


<commit_before>// Copyright 2013 tsuru 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 action

import (
	"errors"
	"github.com/globocom/tsuru/log"
)

// Result is the value returned by Forward. It is used in the call of the next
// action, and also when rolling back the actions.
type Result interface{}

// Forward is the function called by the pipeline executor in the forward
// phase.  It receives a FWContext instance, that contains the list of
// parameters given to the pipeline executor and the result of the previous
// action in the pipeline (which will be nil for the first action in the
// pipeline).
type Forward func(context FWContext) (Result, error)

// Backward is the function called by the pipeline executor when in the
// backward phase. It receives the context instance, that contains the list of
// parameters given to the pipeline executor and the result of the forward
// phase.
type Backward func(context BWContext)

// FWContext is the context used in calls to Forward functions (forward phase).
type FWContext struct {
	// Result of the previous action.
	Previous Result

	// List of parameters given to the executor.
	Params []interface{}
}

// BWContext is the context used in calls to Backward functions (backward
// phase).
type BWContext struct {
	// Result of the forward phase (for the current action).
	FWResult Result

	// List of parameters given to the executor.
	Params []interface{}
}

// Action defines actions that should be . It is composed of two functions:
// Forward and Backward.
//
// Each action should do only one thing, and do it well. All information that
// is needed to undo the action should be returned by the Forward function.
type Action struct {
	// Name is the action name. Used by the log.
	Name string

	// Function that will be invoked in the forward phase. This value
	// cannot be nil.
	Forward Forward

	// Function that will be invoked in the backward phase. For actions
	// that are not undoable, this attribute should be nil.
	Backward Backward

	// Minimum number of parameters that this action requires to run.
	MinParams int

	// Result of the action. Stored for use in the backward phase.
	result Result
}

// Pipeline is a list of actions. Each pipeline is atomic: either all actions
// are successfully executed, or none of them are. For that, it's fundamental
// that all actions are really small and atomic.
type Pipeline struct {
	actions []*Action
}

// NewPipeline creates a new pipeline instance with the given list of actions.
func NewPipeline(actions ...*Action) *Pipeline {
	return &Pipeline{actions: actions}
}

// Execute executes the pipeline.
//
// The execution starts in the forward phase, calling the Forward function of
// all actions. If none of the Forward calls return error, the pipeline
// execution ends in the forward phase and is "committed".
//
// If any of the Forward call fail, the executor switches to the backward phase
// (roll back) and call the Backward function for each action completed. It
// does not call the Backward function of the action that has failed.
//
// After rolling back all completed actions, it returns the original error
// returned by the action that failed.
func (p *Pipeline) Execute(params ...interface{}) error {
	var (
		r   Result
		err error
	)
	if len(p.actions) == 0 {
		return errors.New("No actions to execute.")
	}
	fwCtx := FWContext{Params: params}
	for i, a := range p.actions {
		log.Printf("[pipeline] running the Forward for the %s action", a.Name)
		if a.Forward == nil {
			err = errors.New("All actions must define the forward function.")
		} else if len(fwCtx.Params) < a.MinParams {
			err = errors.New("Not enough parameters to call Action.Forward.")
		} else {
			r, err = a.Forward(fwCtx)
			a.result = r
			fwCtx.Previous = r
		}
		if err != nil {
			p.rollback(i-1, params)
			return err
		}
	}
	return nil
}

func (p *Pipeline) rollback(index int, params []interface{}) {
	bwCtx := BWContext{Params: params}
	for i := index; i >= 0; i-- {
		log.Printf("[pipeline] running the Backward for the %s action", p.actions[i].Name)
		if p.actions[i].Backward != nil {
			bwCtx.FWResult = p.actions[i].result
			p.actions[i].Backward(bwCtx)
		}
	}
}
<commit_msg>actions: changed log message.<commit_after>// Copyright 2013 tsuru 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 action

import (
	"errors"
	"github.com/globocom/tsuru/log"
)

// Result is the value returned by Forward. It is used in the call of the next
// action, and also when rolling back the actions.
type Result interface{}

// Forward is the function called by the pipeline executor in the forward
// phase.  It receives a FWContext instance, that contains the list of
// parameters given to the pipeline executor and the result of the previous
// action in the pipeline (which will be nil for the first action in the
// pipeline).
type Forward func(context FWContext) (Result, error)

// Backward is the function called by the pipeline executor when in the
// backward phase. It receives the context instance, that contains the list of
// parameters given to the pipeline executor and the result of the forward
// phase.
type Backward func(context BWContext)

// FWContext is the context used in calls to Forward functions (forward phase).
type FWContext struct {
	// Result of the previous action.
	Previous Result

	// List of parameters given to the executor.
	Params []interface{}
}

// BWContext is the context used in calls to Backward functions (backward
// phase).
type BWContext struct {
	// Result of the forward phase (for the current action).
	FWResult Result

	// List of parameters given to the executor.
	Params []interface{}
}

// Action defines actions that should be . It is composed of two functions:
// Forward and Backward.
//
// Each action should do only one thing, and do it well. All information that
// is needed to undo the action should be returned by the Forward function.
type Action struct {
	// Name is the action name. Used by the log.
	Name string

	// Function that will be invoked in the forward phase. This value
	// cannot be nil.
	Forward Forward

	// Function that will be invoked in the backward phase. For actions
	// that are not undoable, this attribute should be nil.
	Backward Backward

	// Minimum number of parameters that this action requires to run.
	MinParams int

	// Result of the action. Stored for use in the backward phase.
	result Result
}

// Pipeline is a list of actions. Each pipeline is atomic: either all actions
// are successfully executed, or none of them are. For that, it's fundamental
// that all actions are really small and atomic.
type Pipeline struct {
	actions []*Action
}

// NewPipeline creates a new pipeline instance with the given list of actions.
func NewPipeline(actions ...*Action) *Pipeline {
	return &Pipeline{actions: actions}
}

// Execute executes the pipeline.
//
// The execution starts in the forward phase, calling the Forward function of
// all actions. If none of the Forward calls return error, the pipeline
// execution ends in the forward phase and is "committed".
//
// If any of the Forward call fail, the executor switches to the backward phase
// (roll back) and call the Backward function for each action completed. It
// does not call the Backward function of the action that has failed.
//
// After rolling back all completed actions, it returns the original error
// returned by the action that failed.
func (p *Pipeline) Execute(params ...interface{}) error {
	var (
		r   Result
		err error
	)
	if len(p.actions) == 0 {
		return errors.New("No actions to execute.")
	}
	fwCtx := FWContext{Params: params}
	for i, a := range p.actions {
		log.Printf("[pipeline] running the Forward for the %s action", a.Name)
		if a.Forward == nil {
			err = errors.New("All actions must define the forward function.")
		} else if len(fwCtx.Params) < a.MinParams {
			err = errors.New("Not enough parameters to call Action.Forward.")
		} else {
			r, err = a.Forward(fwCtx)
			a.result = r
			fwCtx.Previous = r
		}
		if err != nil {
			p.rollback(i-1, params)
			return err
		}
	}
	return nil
}

func (p *Pipeline) rollback(index int, params []interface{}) {
	bwCtx := BWContext{Params: params}
	for i := index; i >= 0; i-- {
		log.Printf("[pipeline] running Backward for %s action", p.actions[i].Name)
		if p.actions[i].Backward != nil {
			bwCtx.FWResult = p.actions[i].result
			p.actions[i].Backward(bwCtx)
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>pkg/api: move Semantic equality to k8s.io/apimachinery/pkg/api/equality<commit_after><|endoftext|>


<commit_before>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// PerUserKeyUpkeep rolls the user's per-user-key if the last PUK
// was added by a now-revoked device.
// Does not add a first per-user-key. Use PerUserKeyUpgrade for that.
// This engine makes up for the fact that after a self-deprovision
// the latest PUK for a user was generated on the very machine they
// wanted to deprovision.
// This will not notice if a device revoked another device but neglected
// to roll the PUK. No clients should do that.
package engine

import (
	"errors"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

// PerUserKeyUpkeep is an engine.
type PerUserKeyUpkeep struct {
	libkb.Contextified
	args       *PerUserKeyUpkeepArgs
	DidRollKey bool
}

type PerUserKeyUpkeepArgs struct {
	LoginContext libkb.LoginContext // optional
}

// NewPerUserKeyUpkeep creates a PerUserKeyUpkeep engine.
func NewPerUserKeyUpkeep(g *libkb.GlobalContext, args *PerUserKeyUpkeepArgs) *PerUserKeyUpkeep {
	return &PerUserKeyUpkeep{
		args:         args,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *PerUserKeyUpkeep) Name() string {
	return "PerUserKeyUpkeep"
}

// GetPrereqs returns the engine prereqs.
func (e *PerUserKeyUpkeep) Prereqs() Prereqs {
	return Prereqs{
		Session: true,
	}
}

// RequiredUIs returns the required UIs.
func (e *PerUserKeyUpkeep) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *PerUserKeyUpkeep) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{}
}

// Run starts the engine.
func (e *PerUserKeyUpkeep) Run(ctx *Context) (err error) {
	defer e.G().CTrace(ctx.GetNetContext(), "PerUserKeyUpkeep", func() error { return err })()
	return e.inner(ctx)
}

func (e *PerUserKeyUpkeep) inner(ctx *Context) error {
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep load self")

	uid := e.G().GetMyUID()
	if uid.IsNil() {
		return libkb.NoUIDError{}
	}

	loadArg := libkb.NewLoadUserArgBase(e.G()).
		WithNetContext(ctx.GetNetContext()).
		WithUID(uid).
		WithSelf(true).
		WithPublicKeyOptional()
	loadArg.LoginContext = e.args.LoginContext
	upak, me, err := e.G().GetUPAKLoader().LoadV2(*loadArg)
	if err != nil {
		return err
	}
	// `me` could be nil.

	var shouldRollKey bool
	shouldRollKey, err = e.shouldRollKey(ctx, uid, &upak.Current)
	if err != nil {
		return err
	}
	if !shouldRollKey {
		e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep skipping")
		return nil
	}

	// Roll the key
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep rolling key")
	arg := &PerUserKeyRollArgs{
		LoginContext: e.args.LoginContext,
		Me:           me,
	}
	eng := NewPerUserKeyRoll(e.G(), arg)
	err = RunEngine(eng, ctx)
	e.DidRollKey = eng.DidNewKey
	return err
}

// Whether we should roll the per-user-key.
func (e *PerUserKeyUpkeep) shouldRollKey(ctx *Context, uid keybase1.UID, upak *keybase1.UserPlusKeysV2) (bool, error) {

	if len(upak.PerUserKeys) == 0 {
		e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep has no per-user-key")
		return false, nil
	}
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep has %v per-user-keys", len(upak.PerUserKeys))

	lastPuk := upak.PerUserKeys[len(upak.PerUserKeys)-1]
	if !lastPuk.SignedByKID.IsValid() {
		return false, errors.New("latest per-user-key had invalid signed-by KID")
	}
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep last key signed by KID: %v", lastPuk.SignedByKID.String())
	return !e.keyIsActiveSibkey(ctx, lastPuk.SignedByKID, upak), nil
}

func (e *PerUserKeyUpkeep) keyIsActiveSibkey(ctx *Context, kid keybase1.KID, upak *keybase1.UserPlusKeysV2) bool {
	for _, dkey := range upak.DeviceKeys {
		active := dkey.Base.Revocation == nil
		if active && dkey.Base.IsSibkey && dkey.Base.Kid.Equal(kid) {
			return true
		}
	}
	return false
}
<commit_msg>feedback: remove logincontexts<commit_after>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// PerUserKeyUpkeep rolls the user's per-user-key if the last PUK
// was added by a now-revoked device.
// Does not add a first per-user-key. Use PerUserKeyUpgrade for that.
// This engine makes up for the fact that after a self-deprovision
// the latest PUK for a user was generated on the very machine they
// wanted to deprovision.
// This will not notice if a device revoked another device but neglected
// to roll the PUK. No clients should do that.
package engine

import (
	"errors"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

// PerUserKeyUpkeep is an engine.
type PerUserKeyUpkeep struct {
	libkb.Contextified
	args       *PerUserKeyUpkeepArgs
	DidRollKey bool
}

type PerUserKeyUpkeepArgs struct{}

// NewPerUserKeyUpkeep creates a PerUserKeyUpkeep engine.
func NewPerUserKeyUpkeep(g *libkb.GlobalContext, args *PerUserKeyUpkeepArgs) *PerUserKeyUpkeep {
	return &PerUserKeyUpkeep{
		args:         args,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *PerUserKeyUpkeep) Name() string {
	return "PerUserKeyUpkeep"
}

// GetPrereqs returns the engine prereqs.
func (e *PerUserKeyUpkeep) Prereqs() Prereqs {
	return Prereqs{
		Session: true,
	}
}

// RequiredUIs returns the required UIs.
func (e *PerUserKeyUpkeep) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *PerUserKeyUpkeep) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{}
}

// Run starts the engine.
func (e *PerUserKeyUpkeep) Run(ctx *Context) (err error) {
	defer e.G().CTrace(ctx.GetNetContext(), "PerUserKeyUpkeep", func() error { return err })()
	return e.inner(ctx)
}

func (e *PerUserKeyUpkeep) inner(ctx *Context) error {
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep load self")

	uid := e.G().GetMyUID()
	if uid.IsNil() {
		return libkb.NoUIDError{}
	}

	loadArg := libkb.NewLoadUserArgBase(e.G()).
		WithNetContext(ctx.GetNetContext()).
		WithUID(uid).
		WithSelf(true).
		WithPublicKeyOptional()
	upak, me, err := e.G().GetUPAKLoader().LoadV2(*loadArg)
	if err != nil {
		return err
	}
	// `me` could be nil.

	var shouldRollKey bool
	shouldRollKey, err = e.shouldRollKey(ctx, uid, &upak.Current)
	if err != nil {
		return err
	}
	if !shouldRollKey {
		e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep skipping")
		return nil
	}

	// Roll the key
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep rolling key")
	arg := &PerUserKeyRollArgs{
		Me: me,
	}
	eng := NewPerUserKeyRoll(e.G(), arg)
	err = RunEngine(eng, ctx)
	e.DidRollKey = eng.DidNewKey
	return err
}

// Whether we should roll the per-user-key.
func (e *PerUserKeyUpkeep) shouldRollKey(ctx *Context, uid keybase1.UID, upak *keybase1.UserPlusKeysV2) (bool, error) {

	if len(upak.PerUserKeys) == 0 {
		e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep has no per-user-key")
		return false, nil
	}
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep has %v per-user-keys", len(upak.PerUserKeys))

	lastPuk := upak.PerUserKeys[len(upak.PerUserKeys)-1]
	if !lastPuk.SignedByKID.IsValid() {
		return false, errors.New("latest per-user-key had invalid signed-by KID")
	}
	e.G().Log.CDebugf(ctx.GetNetContext(), "PerUserKeyUpkeep last key signed by KID: %v", lastPuk.SignedByKID.String())
	return !e.keyIsActiveSibkey(ctx, lastPuk.SignedByKID, upak), nil
}

func (e *PerUserKeyUpkeep) keyIsActiveSibkey(ctx *Context, kid keybase1.KID, upak *keybase1.UserPlusKeysV2) bool {
	for _, dkey := range upak.DeviceKeys {
		active := dkey.Base.Revocation == nil
		if active && dkey.Base.IsSibkey && dkey.Base.Kid.Equal(kid) {
			return true
		}
	}
	return false
}
<|endoftext|>