<commit_before>package main

import (
	"fmt"
	"image"
	_ "image/png"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/generaltso/vibrant"
	"gopkg.in/gographics/imagick.v3/imagick"
)

func getImageColors(file string) (fgColor, bgColor string) {
	checkErr := func(err error) {
		if err != nil {
			panic(err)
		}
	}

	f, err := os.Open(file)
	checkErr(err)
	defer f.Close()

	img, _, err := image.Decode(f)
	checkErr(err)

	palette, err := vibrant.NewPaletteFromImage(img)
	checkErr(err)

	for name, swatch := range palette.ExtractAwesome() {
		if name == "Vibrant" {
			fgColor = swatch.Color.TitleTextColor().RGBHex()
			bgColor = swatch.Color.RGBHex()
		}
	}
	if fgColor == bgColor {
		fgColor = "#000"
	}
	return fgColor, bgColor
}

func createStoryText(fgColor, bgColor, text string) *imagick.MagickWand {
	mw := imagick.NewMagickWand()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	pw.SetColor("none")
	mw.NewImage(1080, 103, pw)

	dw.SetFont("TTCommonsB")
	dw.SetFontSize(80)

	textMetrics := mw.QueryFontMetrics(dw, text)

	textWidth := textMetrics.TextWidth

	pw.SetColor(bgColor)
	dw.SetFillColor(pw)

	dw.RoundRectangle((1080-textWidth)/2-20, 0, (1080-textWidth)/2+textWidth+20, 103, 10, 10)

	pw.SetColor(fgColor)
	dw.SetFillColor(pw)
	dw.SetTextAlignment(imagick.ALIGN_CENTER)
	dw.Annotation(1080/2, 93-17, text)

	mw.DrawImage(dw)

	return mw
}

func downloadTeamLogo(filepath, url string) (err error) {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Check server response
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status: %s", resp.Status)
	}

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func getTeamsLogo(radiantTeamID, direTeamID int) {
	teamsData := getTeamsData()
	for _, team := range teamsData {
		teamID := team.TeamID
		if teamID == radiantTeamID || teamID == direTeamID {
			err := downloadTeamLogo(fmt.Sprintf("tmp/teams/%d.png", teamID), team.LogoURL)
			if err != nil {
				log.Printf("Error downloading %d team logo", teamID)
			}
		}
	}
}

func makeStoryImage(matchData oDotaMatchData) {
	radiantTeamName := matchData.RadiantName
	direTeamName := matchData.DireName
	radiantTeamID := matchData.RadiantTeamID
	direTeamID := matchData.DireTeamID

	getTeamsLogo(radiantTeamID, direTeamID)

	direTeamFile := fmt.Sprintf("tmp/teams/%d.png", direTeamID)
	radiantTeamFile := fmt.Sprintf("tmp/teams/%d.png", radiantTeamID)

	mw := imagick.NewMagickWand()
	defer mw.Destroy()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	dw.SetFont("TTCommonsB")

	pw.SetColor("#242424")
	mw.NewImage(1080, 1920, pw)

	fgColor, bgColor := getImageColors(direTeamFile)

	direTeamText := createStoryText(fgColor, bgColor, direTeamName)

	mw.CompositeImage(direTeamText, imagick.COMPOSITE_OP_OVER, true, 0, ((1920/2)-294)/2+250)

	direTeamText.Destroy()

	fgColor, bgColor = getImageColors(radiantTeamFile)

	radiantTeamText := createStoryText(fgColor, bgColor, radiantTeamName)

	mw.CompositeImage(radiantTeamText, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2+((1920/2)-294)/2+250)

	radiantTeamText.Destroy()

	direTeamLogo := imagick.NewMagickWand()
	direTeamLogo.ReadImage(direTeamFile)
	mw.CompositeImage(direTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, ((1920/2)-294)/2-100)
	direTeamLogo.Destroy()

	radiantTeamLogo := imagick.NewMagickWand()
	radiantTeamLogo.ReadImage(radiantTeamFile)
	mw.CompositeImage(radiantTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, 1920/2+((1920/2)-294)/2-100)
	radiantTeamLogo.Destroy()

	vs := createStoryText("#242424", "#ffffff", "VS")
	mw.CompositeImage(vs, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2-51)
	vs.Destroy()

	mw.DrawImage(dw)
	mw.WriteImage("test.png")
}
<commit_msg>Fix images<commit_after>package main

import (
	"fmt"
	"image"
	_ "image/png"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/generaltso/vibrant"
	"gopkg.in/gographics/imagick.v3/imagick"
)

func getImageColors(file string) (fgColor, bgColor string) {
	checkErr := func(err error) {
		if err != nil {
			panic(err)
		}
	}

	f, err := os.Open(file)
	checkErr(err)
	defer f.Close()

	img, _, err := image.Decode(f)
	checkErr(err)

	palette, err := vibrant.NewPaletteFromImage(img)
	checkErr(err)

	for name, swatch := range palette.ExtractAwesome() {
		if name == "Vibrant" {
			fgColor = swatch.Color.TitleTextColor().RGBHex()
			bgColor = swatch.Color.RGBHex()
		}
	}
	if fgColor == bgColor {
		fgColor = "#000"
	}
	return fgColor, bgColor
}

func createStoryText(fgColor, bgColor, text string) *imagick.MagickWand {
	mw := imagick.NewMagickWand()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	pw.SetColor("none")
	mw.NewImage(1080, 103, pw)

	dw.SetFont("TTCommonsB")
	dw.SetFontSize(80)

	textMetrics := mw.QueryFontMetrics(dw, text)

	textWidth := textMetrics.TextWidth

	pw.SetColor(bgColor)
	dw.SetFillColor(pw)

	dw.RoundRectangle((1080-textWidth)/2-20, 0, (1080-textWidth)/2+textWidth+20, 103, 10, 10)

	pw.SetColor(fgColor)
	dw.SetFillColor(pw)
	dw.SetTextAlignment(imagick.ALIGN_CENTER)
	dw.Annotation(1080/2, 93-17, text)

	mw.DrawImage(dw)

	return mw
}

func downloadTeamLogo(filepath, url string) (err error) {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Check server response
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status: %s", resp.Status)
	}

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func getTeamsLogo(radiantTeamID, direTeamID int) {
	teamsData := getTeamsData()
	for _, team := range teamsData {
		teamID := team.TeamID
		if teamID == radiantTeamID || teamID == direTeamID {
			err := downloadTeamLogo(fmt.Sprintf("tmp/teams/%d.png", teamID), team.LogoURL)
			if err != nil {
				log.Printf("Error downloading %d team logo", teamID)
			}
		}
	}
}

func makeStoryImage(matchData oDotaMatchData) {
	radiantTeamName := matchData.RadiantName
	direTeamName := matchData.DireName
	radiantTeamID := matchData.RadiantTeamID
	direTeamID := matchData.DireTeamID

	getTeamsLogo(radiantTeamID, direTeamID)

	direTeamFile := fmt.Sprintf("tmp/teams/%d.png", direTeamID)
	radiantTeamFile := fmt.Sprintf("tmp/teams/%d.png", radiantTeamID)

	mw := imagick.NewMagickWand()
	defer mw.Destroy()
	dw := imagick.NewDrawingWand()
	defer dw.Destroy()
	pw := imagick.NewPixelWand()
	defer pw.Destroy()

	dw.SetFont("TTCommonsB")

	pw.SetColor("#242424")
	mw.NewImage(1080, 1920, pw)

	fgColor, bgColor := getImageColors(direTeamFile)

	direTeamText := createStoryText(fgColor, bgColor, direTeamName)

	mw.CompositeImage(direTeamText, imagick.COMPOSITE_OP_OVER, true, 0, ((1920/2)-294)/2+250)

	direTeamText.Destroy()

	fgColor, bgColor = getImageColors(radiantTeamFile)

	radiantTeamText := createStoryText(fgColor, bgColor, radiantTeamName)

	mw.CompositeImage(radiantTeamText, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2+((1920/2)-294)/2+250)

	radiantTeamText.Destroy()

	direTeamLogo := imagick.NewMagickWand()
	direTeamLogo.ReadImage(direTeamFile)
	direTeamLogo.ResizeImage(294, 294, imagick.FILTER_CUBIC)
	mw.CompositeImage(direTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, ((1920/2)-294)/2-100)
	direTeamLogo.Destroy()

	radiantTeamLogo := imagick.NewMagickWand()
	radiantTeamLogo.ReadImage(radiantTeamFile)
	radiantTeamLogo.ResizeImage(294, 294, imagick.FILTER_CUBIC)
	mw.CompositeImage(radiantTeamLogo, imagick.COMPOSITE_OP_OVER, true, (1080-294)/2, 1920/2+((1920/2)-294)/2-100)
	radiantTeamLogo.Destroy()

	vs := createStoryText("#242424", "#ffffff", "VS")
	mw.CompositeImage(vs, imagick.COMPOSITE_OP_OVER, true, 0, 1920/2-51)
	vs.Destroy()

	mw.DrawImage(dw)
	mw.WriteImage("test.png")
}
<|endoftext|>


<commit_before>package consul

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"reflect"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/chennqqi/goutils/closeevent"
	"github.com/chennqqi/goutils/utils"
	"github.com/chennqqi/goutils/yamlconfig"
	"gopkg.in/yaml.v2"
)

type ConsulApp struct {
	*ConsulOperator
}

func NewAppWithCustomCfg(cfg interface{}, confName, healthHost string) (*ConsulApp, error) {
	var capp ConsulApp
	appName := utils.ApplicationName()
	consulapi := NewConsulOp("")
	consulapi.Fix()
	capp.ConsulOperator = consulapi

	if err := consulapi.Ping(); err != nil {
		logrus.Error("[main] ping consul failed, try local")

		if confName == "" {
			confName = fmt.Sprintf("%s.yml", appName)
		}
		err := yamlconfig.Load(cfg, confName)
		if os.IsNotExist(err) {
			fmt.Println("configure not exist, make default")
			yamlconfig.Save(cfg, confName)
			return nil, err
		} else if err != nil {
			logrus.Error("[main:main] Load yml config error")
			return nil, err
		}
	} else {
		if confName == "" {
			confName = fmt.Sprintf("config/%s.yml", appName)
		}
		txt, err := consulapi.Get(confName)
		if err == nil {
			yaml.Unmarshal(txt, cfg)
		} else {
			logrus.Errorf("[main:main] Load conf(%v) from consul error: %v", confName, err)
			err = yamlconfig.Load(cfg, confName)
			if err != nil {
				fmt.Println("make empty local config")
				yamlconfig.Save(cfg, confName)
				return nil, errors.New("make empty local config")
			}
		}
	}

	//post fix consul
	if healthHost == "" && cfg != nil {
		st := reflect.ValueOf(cfg).Elem()
		field := st.FieldByName("HealthHost")
		if !field.IsValid() {
			return nil, errors.New("cfg not contains`HealthHost")
		}
		healthHost = field.String()
	}
	if healthHost == "" {
		return nil, errors.New("cfg or HealthHost must be valid")
	}

	{
		consulapi.Name = appName
		v := strings.Split(healthHost, ":")
		if len(v) > 1 {
			fmt.Sscanf(v[1], "%d", &consulapi.Port)
		}
	}
	return &capp, nil
}

func NewAppWithCfg(cfg interface{}, healthHost string) (*ConsulApp, error) {
	return NewAppWithCustomCfg(cfg, "", healthHost)
}

func NewApp(healthHost string) (*ConsulApp, error) {
	//post fix consul
	if healthHost == "" {
		return nil, errors.New("healHost must be valid")
	}

	var capp ConsulApp
	appName := utils.ApplicationName()
	consulapi := NewConsulOp("")
	consulapi.Fix()
	capp.ConsulOperator = consulapi

	if err := consulapi.Ping(); err != nil {
		logrus.Error("[main] ping consul failed, try local")
		return nil, err
	}

	{
		consulapi.Name = appName
		v := strings.Split(healthHost, ":")
		if len(v) > 1 {
			fmt.Sscanf(v[1], "%d", &consulapi.Port)
		}
	}
	return &capp, nil
}

func (c *ConsulApp) Wait(stopcall func(os.Signal), signals ...os.Signal) {
	quitChan := make(chan os.Signal, 1)
	defer close(quitChan)
	if len(signals) > 0 {
		signal.Notify(quitChan, signals...)
	} else {
		closeevent.CloseNotify(quitChan)
	}

	c.RegisterService()
	sig := <-quitChan
	logrus.Info("[main:main] quit, recv signal ", sig)
	if stopcall != nil {
		stopcall(sig)
	}
	c.DeregisterService()
}
<commit_msg>update out debug<commit_after>package consul

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"reflect"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/chennqqi/goutils/closeevent"
	"github.com/chennqqi/goutils/utils"
	"github.com/chennqqi/goutils/yamlconfig"
	"gopkg.in/yaml.v2"
)

type ConsulApp struct {
	*ConsulOperator
}

func NewAppWithCustomCfg(cfg interface{}, confName, healthHost string) (*ConsulApp, error) {
	var capp ConsulApp
	appName := utils.ApplicationName()
	consulapi := NewConsulOp("")
	consulapi.Fix()
	capp.ConsulOperator = consulapi
	if confName == "" {
		confName = fmt.Sprintf("%s.yml", appName)
	}

	if err := consulapi.Ping(); err != nil {
		logrus.Error("[consul/app.go]  ping consul failed, try local")
		err := yamlconfig.Load(cfg, confName)
		if os.IsNotExist(err) {
			fmt.Println("configure not exist, make default")
			yamlconfig.Save(cfg, confName)
			return nil, err
		} else if err != nil {
			logrus.Errorf("[consul/app.go] Load %v config from local error: %v", confName, err)
			return nil, err
		}
	} else {
		txt, err := consulapi.Get(confName)
		if err == nil {
			yaml.Unmarshal(txt, cfg)
		} else {
			logrus.Errorf("[consul/app.go] Load conf(%v) from consul error: %v", confName, err)
			err = yamlconfig.Load(cfg, confName)
			if err != nil {
				fmt.Println("make empty local config")
				yamlconfig.Save(cfg, confName)
				return nil, errors.New("make empty local config")
			}
		}
	}

	//post fix consul
	if healthHost == "" && cfg != nil {
		st := reflect.ValueOf(cfg).Elem()
		field := st.FieldByName("HealthHost")
		if !field.IsValid() {
			return nil, errors.New("cfg not contains`HealthHost")
		}
		healthHost = field.String()
	}
	if healthHost == "" {
		return nil, errors.New("cfg or HealthHost must be valid")
	}

	{
		consulapi.Name = appName
		v := strings.Split(healthHost, ":")
		if len(v) > 1 {
			fmt.Sscanf(v[1], "%d", &consulapi.Port)
		}
	}
	return &capp, nil
}

func NewAppWithCfg(cfg interface{}, healthHost string) (*ConsulApp, error) {
	return NewAppWithCustomCfg(cfg, "", healthHost)
}

func NewApp(healthHost string) (*ConsulApp, error) {
	//post fix consul
	if healthHost == "" {
		return nil, errors.New("healHost must be valid")
	}

	var capp ConsulApp
	appName := utils.ApplicationName()
	consulapi := NewConsulOp("")
	consulapi.Fix()
	capp.ConsulOperator = consulapi

	if err := consulapi.Ping(); err != nil {
		logrus.Error("[main] ping consul failed, try local")
		return nil, err
	}

	{
		consulapi.Name = appName
		v := strings.Split(healthHost, ":")
		if len(v) > 1 {
			fmt.Sscanf(v[1], "%d", &consulapi.Port)
		}
	}
	return &capp, nil
}

func (c *ConsulApp) Wait(stopcall func(os.Signal), signals ...os.Signal) {
	quitChan := make(chan os.Signal, 1)
	defer close(quitChan)
	if len(signals) > 0 {
		signal.Notify(quitChan, signals...)
	} else {
		closeevent.CloseNotify(quitChan)
	}

	c.RegisterService()
	sig := <-quitChan
	logrus.Info("[main:main] quit, recv signal ", sig)
	if stopcall != nil {
		stopcall(sig)
	}
	c.DeregisterService()
}
<|endoftext|>


<commit_before>package channel

import (
	"encoding/hex"
	"errors"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/codedust/go-tox"
	"github.com/xamino/tox-dynboot"
)

/*
Channel is a wrapper of the gotox wrapper that creates and manages the underlying Tox
instance.

TODO all callbacks will block, need to avoid that especially when user interaction is required
*/
type Channel struct {
	tox                *gotox.Tox
	callbacks          Callbacks
	wg                 sync.WaitGroup
	stop               chan bool
	transfers          map[uint32]*os.File
	transfersFilesizes map[uint32]uint64
}

/*
Create and starts a new tox channel that continously runs in the background
until this object is destroyed.
*/
func Create(name string, toxdata []byte, callbacks Callbacks) (*Channel, error) {
	if name == "" {
		return nil, errors.New("CreateChannel called with no name!")
	}
	var init bool
	var channel = &Channel{}
	var options *gotox.Options
	var err error

	// prepare for file transfers
	channel.transfers = make(map[uint32]*os.File)
	channel.transfersFilesizes = make(map[uint32]uint64)

	// this decides whether we are initiating a new connection or using an existing one
	if toxdata == nil {
		options = &gotox.Options{
			true, true,
			gotox.TOX_PROXY_TYPE_NONE, "127.0.0.1", 5555, 0, 0, 0,
			gotox.TOX_SAVEDATA_TYPE_NONE, nil}
		init = true
	} else {
		options = &gotox.Options{
			true, true,
			gotox.TOX_PROXY_TYPE_NONE, "127.0.0.1", 5555, 0, 0, 0,
			gotox.TOX_SAVEDATA_TYPE_TOX_SAVE, toxdata}
		init = false
	}
	channel.tox, err = gotox.New(options)
	if err != nil {
		return nil, err
	}
	if init {
		channel.tox.SelfSetName(name)
		channel.tox.SelfSetStatusMessage("Tin Peer")
	}
	err = channel.tox.SelfSetStatus(gotox.TOX_USERSTATUS_NONE)
	// Register our callbacks
	channel.tox.CallbackFriendRequest(channel.onFriendRequest)
	channel.tox.CallbackFriendMessage(channel.onFriendMessage)
	channel.tox.CallbackFileRecvControl(channel.onFileRecvControl)
	channel.tox.CallbackFileRecv(channel.onFileRecv)
	channel.tox.CallbackFileRecvChunk(channel.onFileRecvChunk)
	// some things must only be done if first start
	if init {
		// Bootstrap
		toxNode, err := toxdynboot.FetchFirstAlive(200 * time.Millisecond)
		if err != nil {
			return nil, err
		}
		err = channel.tox.Bootstrap(toxNode.IPv4, toxNode.Port, toxNode.PublicKey)
		if err != nil {
			return nil, err
		}
	}
	// register callbacks
	channel.callbacks = callbacks
	// now to run it:
	channel.wg.Add(1)
	channel.stop = make(chan bool, 1)
	go channel.run()
	return channel, nil
}

// --- public methods here ---

/*
Close shuts down the channel.
*/
func (channel *Channel) Close() {
	// send stop signal
	channel.stop <- false
	// wait for it to close
	channel.wg.Wait()
	// kill tox
	channel.tox.Kill()
}

/*
Address of the Tox instance.
*/
func (channel *Channel) Address() (string, error) {
	address, err := channel.tox.SelfGetAddress()
	if err != nil {
		return "", err
	}
	return strings.ToUpper(hex.EncodeToString(address)), nil
}

/*
ToxData returns the underlying current representation of the tox data. Can be
used to store a Tox instance to disk.
*/
func (channel *Channel) ToxData() ([]byte, error) {
	return channel.tox.GetSavedata()
}

/*
Send a message to the given peer address.
*/
func (channel *Channel) Send(address, message string) error {
	key, err := hex.DecodeString(address)
	if err != nil {
		return err
	}
	id, err := channel.tox.FriendByPublicKey(key)
	if err != nil {
		return err
	}
	// returns message ID but we currently don't use it
	_, err = channel.tox.FriendSendMessage(id, gotox.TOX_MESSAGE_TYPE_NORMAL, message)
	return err
}

/*
AcceptConnection accepts the given address as a connection partner.
*/
func (channel *Channel) AcceptConnection(address string) error {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return err
	}
	// ignore friendnumber
	_, err = channel.tox.FriendAddNorequest(publicKey)
	return err
}

/*
RequestConnection sends a friend request to the given address with the sending
peer information as the message for bootstrapping.
*/
func (channel *Channel) RequestConnection(address, message string) error {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return err
	}
	// send non blocking friend request
	_, err = channel.tox.FriendAdd(publicKey, message)
	return err
}

/*
IsOnline checks whether the given address is currently reachable.
*/
func (channel *Channel) IsOnline(address string) (bool, error) {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return false, err
	}
	num, err := channel.tox.FriendByPublicKey(publicKey)
	if err != nil {
		return false, err
	}
	status, err := channel.tox.FriendGetConnectionStatus(num)
	if err != nil {
		return false, err
	}
	return status != gotox.TOX_CONNECTION_NONE, nil
}

/*
NameOf the key associated to the given address.
*/
func (channel *Channel) NameOf(address string) (string, error) {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return "", err
	}
	num, err := channel.tox.FriendByPublicKey(publicKey)
	if err != nil {
		return "", err
	}
	name, err := channel.tox.FriendGetName(num)
	if err != nil {
		return "", err
	}
	return name, nil
}

// --- private methods here ---

/*
run is the background go routine method that keeps the Tox instance iterating
until Close() is called.
*/
func (channel *Channel) run() {
	for {
		temp, _ := channel.tox.IterationInterval()
		intervall := time.Duration(temp) * time.Millisecond
		select {
		case <-channel.stop:
			channel.wg.Done()
			return
		case <-time.Tick(intervall):
			err := channel.tox.Iterate()
			if err != nil {
				// TODO what do we do here? Can we cleanly close the channel and
				// catch the error further up?
				log.Println(err.Error())
			}
		} // select
	} // for
}

/*
addressOf given friend number.
*/
func (channel *Channel) addressOf(friendnumber uint32) (string, error) {
	publicKey, err := channel.tox.FriendGetPublickey(friendnumber)
	if err != nil {
		return "", errLostAddress
	}
	return hex.EncodeToString(publicKey), nil
}

/*
onFriendRequest calls the appropriate callback, wrapping it sanely for our purposes.
*/
func (channel *Channel) onFriendRequest(t *gotox.Tox, publicKey []byte, message string) {
	if channel.callbacks != nil {
		channel.callbacks.OnNewConnection(hex.EncodeToString(publicKey), message)
	} else {
		log.Println("Error: callbacks are nil!")
	}
}

/*
onFriendMessage calls the appropriate callback, wrapping it sanely for our purposes.
*/
func (channel *Channel) onFriendMessage(t *gotox.Tox, friendnumber uint32, messagetype gotox.ToxMessageType, message string) {
	/*TODO make sensible*/
	if messagetype == gotox.TOX_MESSAGE_TYPE_NORMAL {
		if channel.callbacks != nil {
			address, err := channel.addressOf(friendnumber)
			if err != nil {
				log.Println(err.Error())
				address = illegalAddress
			}
			channel.callbacks.OnMessage(address, message)
		} else {
			log.Println("Error: callbacks are nil!")
		}
	}
}

/*
TODO implement and comment
*/
func (channel *Channel) onFileRecvControl(t *gotox.Tox, friendnumber uint32, filenumber uint32, fileControl gotox.ToxFileControl) {
	log.Printf("File control: %#+v\n", fileControl)
	if fileControl == gotox.TOX_FILE_CONTROL_CANCEL {
		log.Println("Transfer was canceled!")
		// free resources
		delete(channel.transfers, filenumber)
		delete(channel.transfersFilesizes, filenumber)
	}
}

/*
TODO implement and comment
*/
func (channel *Channel) onFileRecv(t *gotox.Tox, friendnumber uint32, filenumber uint32, kind gotox.ToxFileKind, filesize uint64, filename string) {
	address, err := channel.addressOf(friendnumber)
	if err != nil {
		log.Println(err.Error())
		address = illegalAddress
	}
	// use callback to check whether to accept from Tinzenite
	accept, path := channel.callbacks.OnAllowFile(address, filename)
	if !accept {
		return
	}
	// Accept any file send request
	t.FileControl(friendnumber, true, filenumber, gotox.TOX_FILE_CONTROL_RESUME, nil)
	// create file at correct location
	/*TODO how are pause & resume handled?*/
	f, _ := os.Create(path)
	// Append f to the map[uint8]*os.File
	channel.transfers[filenumber] = f
	channel.transfersFilesizes[filenumber] = filesize
}

/*
TODO implement and comment
*/
func (channel *Channel) onFileRecvChunk(t *gotox.Tox, friendnumber uint32, filenumber uint32, position uint64, data []byte) {
	// Write data to the hopefully valid *File handle
	if f, exists := channel.transfers[filenumber]; exists {
		f.WriteAt(data, (int64)(position))
	} else {
		log.Println("File doesn't seem to exist!")
		return
	}
	// this means the file has been completey received
	if position == channel.transfersFilesizes[filenumber] {
		// ensure file is written
		f := channel.transfers[filenumber]
		f.Sync()
		f.Close()
		// free resources
		delete(channel.transfers, filenumber)
		delete(channel.transfersFilesizes, filenumber)
		// can I read a name of a closed file?
		channel.callbacks.OnFileReceived(f.Name())
	}
}
<commit_msg>file name can not be read once file is closed<commit_after>package channel

import (
	"encoding/hex"
	"errors"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/codedust/go-tox"
	"github.com/xamino/tox-dynboot"
)

/*
Channel is a wrapper of the gotox wrapper that creates and manages the underlying Tox
instance.

TODO all callbacks will block, need to avoid that especially when user interaction is required
*/
type Channel struct {
	tox                *gotox.Tox
	callbacks          Callbacks
	wg                 sync.WaitGroup
	stop               chan bool
	transfers          map[uint32]*os.File
	transfersFilesizes map[uint32]uint64
}

/*
Create and starts a new tox channel that continously runs in the background
until this object is destroyed.
*/
func Create(name string, toxdata []byte, callbacks Callbacks) (*Channel, error) {
	if name == "" {
		return nil, errors.New("CreateChannel called with no name!")
	}
	var init bool
	var channel = &Channel{}
	var options *gotox.Options
	var err error

	// prepare for file transfers
	channel.transfers = make(map[uint32]*os.File)
	channel.transfersFilesizes = make(map[uint32]uint64)

	// this decides whether we are initiating a new connection or using an existing one
	if toxdata == nil {
		options = &gotox.Options{
			true, true,
			gotox.TOX_PROXY_TYPE_NONE, "127.0.0.1", 5555, 0, 0, 0,
			gotox.TOX_SAVEDATA_TYPE_NONE, nil}
		init = true
	} else {
		options = &gotox.Options{
			true, true,
			gotox.TOX_PROXY_TYPE_NONE, "127.0.0.1", 5555, 0, 0, 0,
			gotox.TOX_SAVEDATA_TYPE_TOX_SAVE, toxdata}
		init = false
	}
	channel.tox, err = gotox.New(options)
	if err != nil {
		return nil, err
	}
	if init {
		channel.tox.SelfSetName(name)
		channel.tox.SelfSetStatusMessage("Tin Peer")
	}
	err = channel.tox.SelfSetStatus(gotox.TOX_USERSTATUS_NONE)
	// Register our callbacks
	channel.tox.CallbackFriendRequest(channel.onFriendRequest)
	channel.tox.CallbackFriendMessage(channel.onFriendMessage)
	channel.tox.CallbackFileRecvControl(channel.onFileRecvControl)
	channel.tox.CallbackFileRecv(channel.onFileRecv)
	channel.tox.CallbackFileRecvChunk(channel.onFileRecvChunk)
	// some things must only be done if first start
	if init {
		// Bootstrap
		toxNode, err := toxdynboot.FetchFirstAlive(200 * time.Millisecond)
		if err != nil {
			return nil, err
		}
		err = channel.tox.Bootstrap(toxNode.IPv4, toxNode.Port, toxNode.PublicKey)
		if err != nil {
			return nil, err
		}
	}
	// register callbacks
	channel.callbacks = callbacks
	// now to run it:
	channel.wg.Add(1)
	channel.stop = make(chan bool, 1)
	go channel.run()
	return channel, nil
}

// --- public methods here ---

/*
Close shuts down the channel.
*/
func (channel *Channel) Close() {
	// send stop signal
	channel.stop <- false
	// wait for it to close
	channel.wg.Wait()
	// kill tox
	channel.tox.Kill()
}

/*
Address of the Tox instance.
*/
func (channel *Channel) Address() (string, error) {
	address, err := channel.tox.SelfGetAddress()
	if err != nil {
		return "", err
	}
	return strings.ToUpper(hex.EncodeToString(address)), nil
}

/*
ToxData returns the underlying current representation of the tox data. Can be
used to store a Tox instance to disk.
*/
func (channel *Channel) ToxData() ([]byte, error) {
	return channel.tox.GetSavedata()
}

/*
Send a message to the given peer address.
*/
func (channel *Channel) Send(address, message string) error {
	key, err := hex.DecodeString(address)
	if err != nil {
		return err
	}
	id, err := channel.tox.FriendByPublicKey(key)
	if err != nil {
		return err
	}
	// returns message ID but we currently don't use it
	_, err = channel.tox.FriendSendMessage(id, gotox.TOX_MESSAGE_TYPE_NORMAL, message)
	return err
}

/*
AcceptConnection accepts the given address as a connection partner.
*/
func (channel *Channel) AcceptConnection(address string) error {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return err
	}
	// ignore friendnumber
	_, err = channel.tox.FriendAddNorequest(publicKey)
	return err
}

/*
RequestConnection sends a friend request to the given address with the sending
peer information as the message for bootstrapping.
*/
func (channel *Channel) RequestConnection(address, message string) error {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return err
	}
	// send non blocking friend request
	_, err = channel.tox.FriendAdd(publicKey, message)
	return err
}

/*
IsOnline checks whether the given address is currently reachable.
*/
func (channel *Channel) IsOnline(address string) (bool, error) {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return false, err
	}
	num, err := channel.tox.FriendByPublicKey(publicKey)
	if err != nil {
		return false, err
	}
	status, err := channel.tox.FriendGetConnectionStatus(num)
	if err != nil {
		return false, err
	}
	return status != gotox.TOX_CONNECTION_NONE, nil
}

/*
NameOf the key associated to the given address.
*/
func (channel *Channel) NameOf(address string) (string, error) {
	publicKey, err := hex.DecodeString(address)
	if err != nil {
		return "", err
	}
	num, err := channel.tox.FriendByPublicKey(publicKey)
	if err != nil {
		return "", err
	}
	name, err := channel.tox.FriendGetName(num)
	if err != nil {
		return "", err
	}
	return name, nil
}

// --- private methods here ---

/*
run is the background go routine method that keeps the Tox instance iterating
until Close() is called.
*/
func (channel *Channel) run() {
	for {
		temp, _ := channel.tox.IterationInterval()
		intervall := time.Duration(temp) * time.Millisecond
		select {
		case <-channel.stop:
			channel.wg.Done()
			return
		case <-time.Tick(intervall):
			err := channel.tox.Iterate()
			if err != nil {
				// TODO what do we do here? Can we cleanly close the channel and
				// catch the error further up?
				log.Println(err.Error())
			}
		} // select
	} // for
}

/*
addressOf given friend number.
*/
func (channel *Channel) addressOf(friendnumber uint32) (string, error) {
	publicKey, err := channel.tox.FriendGetPublickey(friendnumber)
	if err != nil {
		return "", errLostAddress
	}
	return hex.EncodeToString(publicKey), nil
}

/*
onFriendRequest calls the appropriate callback, wrapping it sanely for our purposes.
*/
func (channel *Channel) onFriendRequest(t *gotox.Tox, publicKey []byte, message string) {
	if channel.callbacks != nil {
		channel.callbacks.OnNewConnection(hex.EncodeToString(publicKey), message)
	} else {
		log.Println("Error: callbacks are nil!")
	}
}

/*
onFriendMessage calls the appropriate callback, wrapping it sanely for our purposes.
*/
func (channel *Channel) onFriendMessage(t *gotox.Tox, friendnumber uint32, messagetype gotox.ToxMessageType, message string) {
	/*TODO make sensible*/
	if messagetype == gotox.TOX_MESSAGE_TYPE_NORMAL {
		if channel.callbacks != nil {
			address, err := channel.addressOf(friendnumber)
			if err != nil {
				log.Println(err.Error())
				address = illegalAddress
			}
			channel.callbacks.OnMessage(address, message)
		} else {
			log.Println("Error: callbacks are nil!")
		}
	}
}

/*
TODO implement and comment
*/
func (channel *Channel) onFileRecvControl(t *gotox.Tox, friendnumber uint32, filenumber uint32, fileControl gotox.ToxFileControl) {
	log.Printf("File control: %#+v\n", fileControl)
	if fileControl == gotox.TOX_FILE_CONTROL_CANCEL {
		log.Println("Transfer was canceled!")
		// free resources
		delete(channel.transfers, filenumber)
		delete(channel.transfersFilesizes, filenumber)
	}
}

/*
TODO implement and comment
*/
func (channel *Channel) onFileRecv(t *gotox.Tox, friendnumber uint32, filenumber uint32, kind gotox.ToxFileKind, filesize uint64, filename string) {
	address, err := channel.addressOf(friendnumber)
	if err != nil {
		log.Println(err.Error())
		address = illegalAddress
	}
	// use callback to check whether to accept from Tinzenite
	accept, path := channel.callbacks.OnAllowFile(address, filename)
	if !accept {
		return
	}
	// accept file send request if we come to here
	t.FileControl(friendnumber, true, filenumber, gotox.TOX_FILE_CONTROL_RESUME, nil)
	// create file at correct location
	/*TODO how are pause & resume handled?*/
	f, _ := os.Create(path)
	// Append f to the map[uint8]*os.File
	channel.transfers[filenumber] = f
	channel.transfersFilesizes[filenumber] = filesize
}

/*
TODO implement and comment
*/
func (channel *Channel) onFileRecvChunk(t *gotox.Tox, friendnumber uint32, filenumber uint32, position uint64, data []byte) {
	// Write data to the hopefully valid *File handle
	if f, exists := channel.transfers[filenumber]; exists {
		f.WriteAt(data, (int64)(position))
	} else {
		log.Println("File doesn't seem to exist!")
		return
	}
	// this means the file has been completey received
	if position == channel.transfersFilesizes[filenumber] {
		// ensure file is written
		f := channel.transfers[filenumber]
		f.Sync()
		name := f.Name()
		f.Close()
		// free resources
		delete(channel.transfers, filenumber)
		delete(channel.transfersFilesizes, filenumber)
		// callback with file name / identification
		channel.callbacks.OnFileReceived(name)
	}
}
<|endoftext|>


<commit_before>package adoc

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// This part contains apis for the containers listed in
// https://docs.docker.com/reference/api/docker_remote_api_v1.17/#21-containers

type Port struct {
	IP          string
	PrivatePort int
	PublicPort  int
	Type        string
}

// Container defines basic container information for ListContainers
type Container struct {
	Command    string
	Created    int64
	Id         string
	Image      string
	Labels     map[string]string
	Names      []string
	Ports      []Port
	SizeRootFs int64
	SizeRw     int64
	Status     string
}

// ContainerConfig defines basic container creation data stucture
type ContainerConfig struct {
	AttachStderr    bool
	AttachStdin     bool
	AttachStdout    bool
	Cmd             []string
	CpuShares       int
	Cpuset          string
	Domainname      string
	Entrypoint      []string
	Env             []string
	ExposedPorts    map[string]struct{}
	Hostname        string
	Image           string
	Labels          map[string]string
	MacAddress      string
	Memory          int64
	MemorySwap      int64
	NetworkDisabled bool
	OnBuild         []string
	OpenStdin       bool
	PortSpecs       []string
	StdinOnce       bool
	Tty             bool
	User            string
	VolumeDriver    string
	Volumes         map[string]struct{}
	WorkingDir      string
}

type Device struct {
	PathOnHost        string
	PathInContainer   string
	CgroupPermissions string
}

type RestartPolicy struct {
	MaximumRetryCount int
	Name              string
}

type Ulimit struct {
	Name string
	Soft int64
	Hard int64
}

type LogConfig struct {
	Type   string
	Config map[string]string
}

// HostConfig defines basic host configuration for container to run
type HostConfig struct {
	Binds           []string
	CapAdd          []string
	CapDrop         []string
	CgroupParent    string
	ContainerIDFile string
	CpuShares       int
	CpusetCpus      string
	Devices         []Device
	Dns             []string
	DnsSearch       []string
	ExtraHosts      []string
	IpcMode         string
	Links           []string
	LxcConf         []map[string]string
	Memory          int64
	MemorySwap      int64
	NetworkMode     string
	PidMode         string
	PortBindings    map[string][]PortBinding
	Privileged      bool
	PublishAllPorts bool
	ReadonlyRootfs  bool
	RestartPolicy   RestartPolicy
	SecurityOpt     []string
	VolumesFrom     []string
	Ulimits         []Ulimit  // 1.18
	LogConfig       LogConfig // 1.18
}

type PortBinding struct {
	HostIp   string
	HostPort string
}

type NetworkSettings struct {
	Bridge                 string
	Gateway                string
	GlobalIPv6Address      string
	GlobalIPv6PrefixLen    int
	IPAddress              string
	IPPrefixLen            int
	IPv6Gateway            string
	LinkLocalIPv6Address   string
	LinkLocalIPv6PrefixLen int
	MacAddress             string
	Ports                  map[string][]PortBinding
}

// ContainerState defines container running state from inspection
type ContainerState struct {
	Dead       bool
	Error      string
	ExitCode   int
	FinishedAt time.Time
	OOMKilled  bool
	Paused     bool
	Pid        int64
	Restarting bool
	Running    bool
	StartedAt  time.Time
}

// SwarmNode defines the swarm api data for container running node
type SwarmNode struct {
	Name   string
	ID     string
	Addr   string
	IP     string
	Cpus   int
	Memory int64
	Labels map[string]string
}

// ContainerDetail defines the detail data of the container from inspection, including the swarm node infor
type ContainerDetail struct {
	AppArmorProfile string
	Args            []string
	Config          ContainerConfig
	Created         time.Time
	Driver          string
	ExecDriver      string
	ExecIDs         []string
	HostConfig      HostConfig
	HostnamePath    string
	HostsPath       string
	Id              string
	Image           string
	LogPath         string
	MountLabel      string
	Name            string
	NetworkSettings NetworkSettings
	Path            string
	ProcessLabel    string
	ResolvConfPath  string
	RestartCount    int
	State           ContainerState
	Volumes         map[string]string
	VolumesRW       map[string]bool
	Node            SwarmNode // swarm api
}

// ListContainers returns containers data, showAll flag defines if you want to show all the containers including the stopped ones
func (client *DockerClient) ListContainers(showAll, showSize bool, filters ...string) ([]Container, error) {
	v := url.Values{}
	v.Set("all", formatBoolToIntString(showAll))
	v.Set("size", formatBoolToIntString(showSize))
	if len(filters) > 0 && filters[0] != "" {
		v.Set("filters", filters[0])
	}
	uri := fmt.Sprintf("containers/json?%s", v.Encode())
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var ret []Container
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

// InspectContainer returns container detail data with container id
func (client *DockerClient) InspectContainer(id string) (ContainerDetail, error) {
	uri := fmt.Sprintf("containers/%s/json", id)
	var ret ContainerDetail
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return ret, err
	} else {
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

func (client *DockerClient) CreateContainer(containerConf ContainerConfig, hostConf HostConfig, name ...string) (string, error) {
	var config struct {
		ContainerConfig
		HostConfig HostConfig
	}
	config.ContainerConfig = containerConf
	config.HostConfig = hostConf

	if body, err := json.Marshal(config); err != nil {
		return "", err
	} else {
		uri := "containers/create"
		if len(name) > 0 && name[0] != "" {
			v := url.Values{}
			v.Set("name", name[0])
			uri += "?" + v.Encode()
		}
		if data, err := client.sendRequest("POST", uri, body, nil, true); err != nil {
			return "", err
		} else {
			var resp struct {
				Id       string
				Warnings []string
			}
			err := json.Unmarshal(data, &resp)
			if len(resp.Warnings) > 0 {
				logger.Warnf("Create container returns warning from docker daemon: %+v", resp.Warnings)
			}
			return resp.Id, err
		}
	}
}

func (client *DockerClient) StartContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/start", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) StopContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/stop", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RestartContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/restart", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) KillContainer(id string, signal ...string) error {
	uri := fmt.Sprintf("containers/%s/kill", id)
	if len(signal) > 0 && signal[0] != "" {
		v := url.Values{}
		v.Set("signal", signal[0])
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) PauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/pause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) UnpauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/unpause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("v", formatBoolToIntString(volumes))
	uri := fmt.Sprintf("containers/%s?%s", id, v.Encode())
	_, err := client.sendRequest("DELETE", uri, nil, nil)
	return err
}

func (client *DockerClient) RenameContainer(id string, name string) error {
	v := url.Values{}
	v.Set("name", name)
	uri := fmt.Sprintf("containers/%s/rename?%s", id, v.Encode())
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

// This will block the call routine until the container is stopped
func (client *DockerClient) WaitContainer(id string) (int, error) {
	uri := fmt.Sprintf("containers/%s/wait", id)
	if data, err := client.sendRequest("POST", uri, nil, nil, true); err != nil {
		return 0, err
	} else {
		var ret map[string]int
		if err := json.Unmarshal(data, &ret); err != nil {
			return 0, err
		}
		if code, ok := ret["StatusCode"]; ok {
			return code, nil
		} else {
			logger.Warnf("There is no StatusCode key inside results map, the API maybe changed, ret=%+v", ret)
			return 0, fmt.Errorf("Cannot get StatusCode from return data, %+v", ret)
		}
	}
}

func (client *DockerClient) ContainerLogs(id string, stdout, stderr, timestamps bool, tail ...int) ([]LogEntry, error) {
	// no following mode
	v := url.Values{}
	v.Set("stdout", formatBoolToIntString(stdout))
	v.Set("stderr", formatBoolToIntString(stderr))
	v.Set("timestamps", formatBoolToIntString(timestamps))
	if len(tail) > 0 && tail[0] >= 0 {
		v.Set("tail", fmt.Sprintf("%d", tail[0]))
	}
	uri := fmt.Sprintf("containers/%s/logs?%s", id, v.Encode())

	var entries []LogEntry
	err := client.sendRequestCallback("GET", uri, nil, nil, func(resp *http.Response) error {
		var cbErr error
		entries, cbErr = ReadAllDockerLogs(resp.Body)
		return cbErr
	})
	return entries, err
}

type Processes struct {
	Titles    []string
	Processes [][]string
}

func (client *DockerClient) ContainerProcesses(id string, psArgs ...string) (Processes, error) {
	var procs Processes
	v := url.Values{}
	if len(psArgs) > 0 && psArgs[0] != "" {
		v.Set("ps_args", psArgs[0])
	}
	uri := fmt.Sprintf("containers/%s/top", id)
	if len(v) > 0 {
		uri += "?" + v.Encode()
	}
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return procs, err
	} else {
		err := json.Unmarshal(data, &procs)
		return procs, err
	}
}

type FsChange struct {
	Path string
	Kind int
}

func (client *DockerClient) ContainerChanges(id string) ([]FsChange, error) {
	uri := fmt.Sprintf("containers/%s/changes", id)
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var changes []FsChange
		err := json.Unmarshal(data, &changes)
		return changes, err
	}
}

// Missing apis for
// containers/(id)/copy
// containers/(id)/attach
// containers/(id)/export
// containers/(id)/resize?h=<height>&w=<width>
// containers/(id)/attach/ws
<commit_msg>add network connect & disconnect<commit_after>package adoc

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// This part contains apis for the containers listed in
// https://docs.docker.com/reference/api/docker_remote_api_v1.17/#21-containers

type Port struct {
	IP          string
	PrivatePort int
	PublicPort  int
	Type        string
}

// Container defines basic container information for ListContainers
type Container struct {
	Command    string
	Created    int64
	Id         string
	Image      string
	Labels     map[string]string
	Names      []string
	Ports      []Port
	SizeRootFs int64
	SizeRw     int64
	Status     string
}

// ContainerConfig defines basic container creation data stucture
type ContainerConfig struct {
	AttachStderr    bool
	AttachStdin     bool
	AttachStdout    bool
	Cmd             []string
	CpuShares       int
	Cpuset          string
	Domainname      string
	Entrypoint      []string
	Env             []string
	ExposedPorts    map[string]struct{}
	Hostname        string
	Image           string
	Labels          map[string]string
	MacAddress      string
	Memory          int64
	MemorySwap      int64
	NetworkDisabled bool
	OnBuild         []string
	OpenStdin       bool
	PortSpecs       []string
	StdinOnce       bool
	Tty             bool
	User            string
	VolumeDriver    string
	Volumes         map[string]struct{}
	WorkingDir      string
}

type Device struct {
	PathOnHost        string
	PathInContainer   string
	CgroupPermissions string
}

type RestartPolicy struct {
	MaximumRetryCount int
	Name              string
}

type Ulimit struct {
	Name string
	Soft int64
	Hard int64
}

type LogConfig struct {
	Type   string
	Config map[string]string
}

// HostConfig defines basic host configuration for container to run
type HostConfig struct {
	Binds           []string
	CapAdd          []string
	CapDrop         []string
	CgroupParent    string
	ContainerIDFile string
	CpuShares       int
	CpusetCpus      string
	Devices         []Device
	Dns             []string
	DnsSearch       []string
	ExtraHosts      []string
	IpcMode         string
	Links           []string
	LxcConf         []map[string]string
	Memory          int64
	MemorySwap      int64
	NetworkMode     string
	PidMode         string
	PortBindings    map[string][]PortBinding
	Privileged      bool
	PublishAllPorts bool
	ReadonlyRootfs  bool
	RestartPolicy   RestartPolicy
	SecurityOpt     []string
	VolumesFrom     []string
	Ulimits         []Ulimit  // 1.18
	LogConfig       LogConfig // 1.18
}

type PortBinding struct {
	HostIp   string
	HostPort string
}

type NetworkSettings struct {
	Bridge                 string
	Gateway                string
	GlobalIPv6Address      string
	GlobalIPv6PrefixLen    int
	IPAddress              string
	IPPrefixLen            int
	IPv6Gateway            string
	LinkLocalIPv6Address   string
	LinkLocalIPv6PrefixLen int
	MacAddress             string
	Ports                  map[string][]PortBinding
}

// ContainerState defines container running state from inspection
type ContainerState struct {
	Dead       bool
	Error      string
	ExitCode   int
	FinishedAt time.Time
	OOMKilled  bool
	Paused     bool
	Pid        int64
	Restarting bool
	Running    bool
	StartedAt  time.Time
}

// SwarmNode defines the swarm api data for container running node
type SwarmNode struct {
	Name   string
	ID     string
	Addr   string
	IP     string
	Cpus   int
	Memory int64
	Labels map[string]string
}

// ContainerDetail defines the detail data of the container from inspection, including the swarm node infor
type ContainerDetail struct {
	AppArmorProfile string
	Args            []string
	Config          ContainerConfig
	Created         time.Time
	Driver          string
	ExecDriver      string
	ExecIDs         []string
	HostConfig      HostConfig
	HostnamePath    string
	HostsPath       string
	Id              string
	Image           string
	LogPath         string
	MountLabel      string
	Name            string
	NetworkSettings NetworkSettings
	Path            string
	ProcessLabel    string
	ResolvConfPath  string
	RestartCount    int
	State           ContainerState
	Volumes         map[string]string
	VolumesRW       map[string]bool
	Node            SwarmNode // swarm api
}

type NetworkOptions struct {
	Container      string
	EndpointConfig EndpointConfig
	Force          bool
}

type EndpointConfig struct {
	IPAMConfig IPAMConfig
}

type IPAMConfig struct {
	IPv4Address string
	IPv6Address string
}

// ListContainers returns containers data, showAll flag defines if you want to show all the containers including the stopped ones
func (client *DockerClient) ListContainers(showAll, showSize bool, filters ...string) ([]Container, error) {
	v := url.Values{}
	v.Set("all", formatBoolToIntString(showAll))
	v.Set("size", formatBoolToIntString(showSize))
	if len(filters) > 0 && filters[0] != "" {
		v.Set("filters", filters[0])
	}
	uri := fmt.Sprintf("containers/json?%s", v.Encode())
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var ret []Container
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

// InspectContainer returns container detail data with container id
func (client *DockerClient) InspectContainer(id string) (ContainerDetail, error) {
	uri := fmt.Sprintf("containers/%s/json", id)
	var ret ContainerDetail
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return ret, err
	} else {
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

func (client *DockerClient) CreateContainer(containerConf ContainerConfig, hostConf HostConfig, name ...string) (string, error) {
	var config struct {
		ContainerConfig
		HostConfig HostConfig
	}
	config.ContainerConfig = containerConf
	config.HostConfig = hostConf

	if body, err := json.Marshal(config); err != nil {
		return "", err
	} else {
		uri := "containers/create"
		if len(name) > 0 && name[0] != "" {
			v := url.Values{}
			v.Set("name", name[0])
			uri += "?" + v.Encode()
		}
		if data, err := client.sendRequest("POST", uri, body, nil, true); err != nil {
			return "", err
		} else {
			var resp struct {
				Id       string
				Warnings []string
			}
			err := json.Unmarshal(data, &resp)
			if len(resp.Warnings) > 0 {
				logger.Warnf("Create container returns warning from docker daemon: %+v", resp.Warnings)
			}
			return resp.Id, err
		}
	}
}

func (client *DockerClient) ConnectContainer(networkName string, id string, ipAddr string) error {
	nc := NetworkOptions
	nc.Container = id
	nc.EndpointConfig.IPAMConfig.IPv4Address = ipAddr
	if body, err := json.Marshal(nc); err != nil {
		return err
	} else {
		url := fmt.Sprintf("networks/%s/connect", networkName)
		_, err := client.sendRequest("POST", uri, body, nil)
		return err
	}
}

func (client *DockerClient) DisconncetContainer(networkName string, id string, force bool) error {
	nc := NetworkOptions
	nc.Container = id
	nc.Force = force
	if body, err := json.Marshal(nc); err != nil {
		return err
	} else {
		url := fmt.Sprintf("networks/%s/disconnect", networkName)
		_, err := client.sendRequest("POST", uri, nil, nil)
		return err
	}
}

func (client *DockerClient) StartContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/start", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) StopContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/stop", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RestartContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/restart", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) KillContainer(id string, signal ...string) error {
	uri := fmt.Sprintf("containers/%s/kill", id)
	if len(signal) > 0 && signal[0] != "" {
		v := url.Values{}
		v.Set("signal", signal[0])
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) PauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/pause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) UnpauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/unpause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("v", formatBoolToIntString(volumes))
	uri := fmt.Sprintf("containers/%s?%s", id, v.Encode())
	_, err := client.sendRequest("DELETE", uri, nil, nil)
	return err
}

func (client *DockerClient) RenameContainer(id string, name string) error {
	v := url.Values{}
	v.Set("name", name)
	uri := fmt.Sprintf("containers/%s/rename?%s", id, v.Encode())
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

// This will block the call routine until the container is stopped
func (client *DockerClient) WaitContainer(id string) (int, error) {
	uri := fmt.Sprintf("containers/%s/wait", id)
	if data, err := client.sendRequest("POST", uri, nil, nil, true); err != nil {
		return 0, err
	} else {
		var ret map[string]int
		if err := json.Unmarshal(data, &ret); err != nil {
			return 0, err
		}
		if code, ok := ret["StatusCode"]; ok {
			return code, nil
		} else {
			logger.Warnf("There is no StatusCode key inside results map, the API maybe changed, ret=%+v", ret)
			return 0, fmt.Errorf("Cannot get StatusCode from return data, %+v", ret)
		}
	}
}

func (client *DockerClient) ContainerLogs(id string, stdout, stderr, timestamps bool, tail ...int) ([]LogEntry, error) {
	// no following mode
	v := url.Values{}
	v.Set("stdout", formatBoolToIntString(stdout))
	v.Set("stderr", formatBoolToIntString(stderr))
	v.Set("timestamps", formatBoolToIntString(timestamps))
	if len(tail) > 0 && tail[0] >= 0 {
		v.Set("tail", fmt.Sprintf("%d", tail[0]))
	}
	uri := fmt.Sprintf("containers/%s/logs?%s", id, v.Encode())

	var entries []LogEntry
	err := client.sendRequestCallback("GET", uri, nil, nil, func(resp *http.Response) error {
		var cbErr error
		entries, cbErr = ReadAllDockerLogs(resp.Body)
		return cbErr
	})
	return entries, err
}

type Processes struct {
	Titles    []string
	Processes [][]string
}

func (client *DockerClient) ContainerProcesses(id string, psArgs ...string) (Processes, error) {
	var procs Processes
	v := url.Values{}
	if len(psArgs) > 0 && psArgs[0] != "" {
		v.Set("ps_args", psArgs[0])
	}
	uri := fmt.Sprintf("containers/%s/top", id)
	if len(v) > 0 {
		uri += "?" + v.Encode()
	}
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return procs, err
	} else {
		err := json.Unmarshal(data, &procs)
		return procs, err
	}
}

type FsChange struct {
	Path string
	Kind int
}

func (client *DockerClient) ContainerChanges(id string) ([]FsChange, error) {
	uri := fmt.Sprintf("containers/%s/changes", id)
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var changes []FsChange
		err := json.Unmarshal(data, &changes)
		return changes, err
	}
}

// Missing apis for
// containers/(id)/copy
// containers/(id)/attach
// containers/(id)/export
// containers/(id)/resize?h=<height>&w=<width>
// containers/(id)/attach/ws
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type ReferenceType int

const (
	ReferenceSymbolic ReferenceType = C.GIT_REF_SYMBOLIC
	ReferenceOid      ReferenceType = C.GIT_REF_OID
)

type Reference struct {
	ptr  *C.git_reference
	repo *Repository
}

type ReferenceCollection struct {
	repo *Repository
}

func (c *ReferenceCollection) Lookup(name string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_reference_lookup(&ptr, c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

func (c *ReferenceCollection) Create(name string, id *Oid, force bool, msg string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_reference_create(&ptr, c.repo.ptr, cname, id.toC(), cbool(force), cmsg)
	runtime.KeepAlive(c)
	runtime.KeepAlive(id)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

func (c *ReferenceCollection) CreateSymbolic(name, target string, force bool, msg string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ctarget := C.CString(target)
	defer C.free(unsafe.Pointer(ctarget))

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_reference_symbolic_create(&ptr, c.repo.ptr, cname, ctarget, cbool(force), cmsg)
	runtime.KeepAlive(c)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

// EnsureLog ensures that there is a reflog for the given reference
// name and creates an empty one if necessary.
func (c *ReferenceCollection) EnsureLog(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_ensure_log(c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

// HasLog returns whether there is a reflog for the given reference
// name
func (c *ReferenceCollection) HasLog(name string) (bool, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_has_log(c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ret < 0 {
		return false, MakeGitError(ret)
	}

	return ret == 1, nil
}

// Dwim looks up a reference by DWIMing its short name
func (c *ReferenceCollection) Dwim(name string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var ptr *C.git_reference
	ret := C.git_reference_dwim(&ptr, c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

func newReferenceFromC(ptr *C.git_reference, repo *Repository) *Reference {
	ref := &Reference{ptr: ptr, repo: repo}
	runtime.SetFinalizer(ref, (*Reference).Free)
	return ref
}

func (v *Reference) SetSymbolicTarget(target string, msg string) (*Reference, error) {
	var ptr *C.git_reference

	ctarget := C.CString(target)
	defer C.free(unsafe.Pointer(ctarget))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, cmsg)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) SetTarget(target *Oid, msg string) (*Reference, error) {
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), cmsg)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) Resolve() (*Reference, error) {
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_resolve(&ptr, v.ptr)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) Rename(name string, force bool, msg string) (*Reference, error) {
	var ptr *C.git_reference
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force), cmsg)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) Target() *Oid {
	ret := newOidFromC(C.git_reference_target(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) SymbolicTarget() string {
	var ret string
	cstr := C.git_reference_symbolic_target(v.ptr)

	if cstr != nil {
		return C.GoString(cstr)
	}

	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Delete() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_delete(v.ptr)
	runtime.KeepAlive(v)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

func (v *Reference) Peel(t ObjectType) (*Object, error) {
	var cobj *C.git_object

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_reference_peel(&cobj, v.ptr, C.git_otype(t))
	runtime.KeepAlive(v)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return allocObject(cobj, v.repo), nil
}

// Owner returns a weak reference to the repository which owns this
// reference.
func (v *Reference) Owner() *Repository {
	return &Repository{
		ptr: C.git_reference_owner(v.ptr),
	}
}

// Cmp compares both references, retursn 0 on equality, otherwise a
// stable sorting.
func (v *Reference) Cmp(ref2 *Reference) int {
	ret := int(C.git_reference_cmp(v.ptr, ref2.ptr))
	runtime.KeepAlive(v)
	runtime.KeepAlive(ref2)
	return ret
}

// Shorthand ret :=s a "human-readable" short reference name
func (v *Reference) Shorthand() string {
	ret := C.GoString(C.git_reference_shorthand(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Name() string {
	ret := C.GoString(C.git_reference_name(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Type() ReferenceType {
	ret := ReferenceType(C.git_reference_type(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) IsBranch() bool {
	ret := C.git_reference_is_branch(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) IsRemote() bool {
	ret := C.git_reference_is_remote(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) IsTag() bool {
	ret := C.git_reference_is_tag(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

// IsNote checks if the reference is a note.
func (v *Reference) IsNote() bool {
	ret := C.git_reference_is_note(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Free() {
	runtime.SetFinalizer(v, nil)
	C.git_reference_free(v.ptr)
}

type ReferenceIterator struct {
	ptr  *C.git_reference_iterator
	repo *Repository
}

type ReferenceNameIterator struct {
	*ReferenceIterator
}

// NewReferenceIterator creates a new iterator over reference names
func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) {
	var ptr *C.git_reference_iterator

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_iterator_new(&ptr, repo.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceIteratorFromC(ptr, repo), nil
}

// NewReferenceIterator creates a new branch iterator over reference names
func (repo *Repository) NewReferenceNameIterator() (*ReferenceNameIterator, error) {
	var ptr *C.git_reference_iterator

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_iterator_new(&ptr, repo.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	iter := newReferenceIteratorFromC(ptr, repo)
	return iter.Names(), nil
}

// NewReferenceIteratorGlob creates an iterator over reference names
// that match the speicified glob. The glob is of the usual fnmatch
// type.
func (repo *Repository) NewReferenceIteratorGlob(glob string) (*ReferenceIterator, error) {
	cstr := C.CString(glob)
	defer C.free(unsafe.Pointer(cstr))
	var ptr *C.git_reference_iterator

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_iterator_glob_new(&ptr, repo.ptr, cstr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceIteratorFromC(ptr, repo), nil
}

func (i *ReferenceIterator) Names() *ReferenceNameIterator {
	return &ReferenceNameIterator{i}
}

// NextName retrieves the next reference name. If the iteration is over,
// the returned error is git.ErrIterOver
func (v *ReferenceNameIterator) Next() (string, error) {
	var ptr *C.char

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_next_name(&ptr, v.ptr)
	if ret < 0 {
		return "", MakeGitError(ret)
	}

	return C.GoString(ptr), nil
}

// Next retrieves the next reference. If the iterationis over, the
// returned error is git.ErrIterOver
func (v *ReferenceIterator) Next() (*Reference, error) {
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_next(&ptr, v.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func newReferenceIteratorFromC(ptr *C.git_reference_iterator, r *Repository) *ReferenceIterator {
	return &ReferenceIterator{
		ptr:  ptr,
		repo: r,
	}
}

// Free the reference iterator
func (v *ReferenceIterator) Free() {
	runtime.SetFinalizer(v, nil)
	C.git_reference_iterator_free(v.ptr)
}

// ReferenceIsValidName ensures the reference name is well-formed.
//
// Valid reference names must follow one of two patterns:
//
// 1. Top-level names must contain only capital letters and underscores,
// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
//
// 2. Names prefixed with "refs/" can be almost anything. You must avoid
// the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences
// ".." and " @ {" which have special meaning to revparse.
func ReferenceIsValidName(name string) bool {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_reference_is_valid_name(cname) == 1 {
		return true
	}
	return false
}
<commit_msg>reference: minor doc improvements<commit_after>package git

/*
#include <git2.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type ReferenceType int

const (
	ReferenceSymbolic ReferenceType = C.GIT_REF_SYMBOLIC
	ReferenceOid      ReferenceType = C.GIT_REF_OID
)

type Reference struct {
	ptr  *C.git_reference
	repo *Repository
}

type ReferenceCollection struct {
	repo *Repository
}

func (c *ReferenceCollection) Lookup(name string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_reference_lookup(&ptr, c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

func (c *ReferenceCollection) Create(name string, id *Oid, force bool, msg string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_reference_create(&ptr, c.repo.ptr, cname, id.toC(), cbool(force), cmsg)
	runtime.KeepAlive(c)
	runtime.KeepAlive(id)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

func (c *ReferenceCollection) CreateSymbolic(name, target string, force bool, msg string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ctarget := C.CString(target)
	defer C.free(unsafe.Pointer(ctarget))

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_reference_symbolic_create(&ptr, c.repo.ptr, cname, ctarget, cbool(force), cmsg)
	runtime.KeepAlive(c)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

// EnsureLog ensures that there is a reflog for the given reference
// name and creates an empty one if necessary.
func (c *ReferenceCollection) EnsureLog(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_ensure_log(c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

// HasLog returns whether there is a reflog for the given reference
// name
func (c *ReferenceCollection) HasLog(name string) (bool, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_has_log(c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ret < 0 {
		return false, MakeGitError(ret)
	}

	return ret == 1, nil
}

// Dwim looks up a reference by DWIMing its short name
func (c *ReferenceCollection) Dwim(name string) (*Reference, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var ptr *C.git_reference
	ret := C.git_reference_dwim(&ptr, c.repo.ptr, cname)
	runtime.KeepAlive(c)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, c.repo), nil
}

func newReferenceFromC(ptr *C.git_reference, repo *Repository) *Reference {
	ref := &Reference{ptr: ptr, repo: repo}
	runtime.SetFinalizer(ref, (*Reference).Free)
	return ref
}

func (v *Reference) SetSymbolicTarget(target string, msg string) (*Reference, error) {
	var ptr *C.git_reference

	ctarget := C.CString(target)
	defer C.free(unsafe.Pointer(ctarget))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, cmsg)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) SetTarget(target *Oid, msg string) (*Reference, error) {
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), cmsg)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) Resolve() (*Reference, error) {
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_resolve(&ptr, v.ptr)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) Rename(name string, force bool, msg string) (*Reference, error) {
	var ptr *C.git_reference
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force), cmsg)
	runtime.KeepAlive(v)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func (v *Reference) Target() *Oid {
	ret := newOidFromC(C.git_reference_target(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) SymbolicTarget() string {
	var ret string
	cstr := C.git_reference_symbolic_target(v.ptr)

	if cstr != nil {
		return C.GoString(cstr)
	}

	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Delete() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_delete(v.ptr)
	runtime.KeepAlive(v)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

func (v *Reference) Peel(t ObjectType) (*Object, error) {
	var cobj *C.git_object

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_reference_peel(&cobj, v.ptr, C.git_otype(t))
	runtime.KeepAlive(v)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return allocObject(cobj, v.repo), nil
}

// Owner returns a weak reference to the repository which owns this
// reference.
func (v *Reference) Owner() *Repository {
	return &Repository{
		ptr: C.git_reference_owner(v.ptr),
	}
}

// Cmp compares v to ref2. It returns 0 on equality, otherwise a
// stable sorting.
func (v *Reference) Cmp(ref2 *Reference) int {
	ret := int(C.git_reference_cmp(v.ptr, ref2.ptr))
	runtime.KeepAlive(v)
	runtime.KeepAlive(ref2)
	return ret
}

// Shorthand returns a "human-readable" short reference name.
func (v *Reference) Shorthand() string {
	ret := C.GoString(C.git_reference_shorthand(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

// Name returns the full name of v.
func (v *Reference) Name() string {
	ret := C.GoString(C.git_reference_name(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Type() ReferenceType {
	ret := ReferenceType(C.git_reference_type(v.ptr))
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) IsBranch() bool {
	ret := C.git_reference_is_branch(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) IsRemote() bool {
	ret := C.git_reference_is_remote(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) IsTag() bool {
	ret := C.git_reference_is_tag(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

// IsNote checks if the reference is a note.
func (v *Reference) IsNote() bool {
	ret := C.git_reference_is_note(v.ptr) == 1
	runtime.KeepAlive(v)
	return ret
}

func (v *Reference) Free() {
	runtime.SetFinalizer(v, nil)
	C.git_reference_free(v.ptr)
}

type ReferenceIterator struct {
	ptr  *C.git_reference_iterator
	repo *Repository
}

type ReferenceNameIterator struct {
	*ReferenceIterator
}

// NewReferenceIterator creates a new iterator over reference names
func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) {
	var ptr *C.git_reference_iterator

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_iterator_new(&ptr, repo.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceIteratorFromC(ptr, repo), nil
}

// NewReferenceIterator creates a new branch iterator over reference names
func (repo *Repository) NewReferenceNameIterator() (*ReferenceNameIterator, error) {
	var ptr *C.git_reference_iterator

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_iterator_new(&ptr, repo.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	iter := newReferenceIteratorFromC(ptr, repo)
	return iter.Names(), nil
}

// NewReferenceIteratorGlob creates an iterator over reference names
// that match the speicified glob. The glob is of the usual fnmatch
// type.
func (repo *Repository) NewReferenceIteratorGlob(glob string) (*ReferenceIterator, error) {
	cstr := C.CString(glob)
	defer C.free(unsafe.Pointer(cstr))
	var ptr *C.git_reference_iterator

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_iterator_glob_new(&ptr, repo.ptr, cstr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceIteratorFromC(ptr, repo), nil
}

func (i *ReferenceIterator) Names() *ReferenceNameIterator {
	return &ReferenceNameIterator{i}
}

// NextName retrieves the next reference name. If the iteration is over,
// the returned error is git.ErrIterOver
func (v *ReferenceNameIterator) Next() (string, error) {
	var ptr *C.char

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_next_name(&ptr, v.ptr)
	if ret < 0 {
		return "", MakeGitError(ret)
	}

	return C.GoString(ptr), nil
}

// Next retrieves the next reference. If the iterationis over, the
// returned error is git.ErrIterOver
func (v *ReferenceIterator) Next() (*Reference, error) {
	var ptr *C.git_reference

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_reference_next(&ptr, v.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return newReferenceFromC(ptr, v.repo), nil
}

func newReferenceIteratorFromC(ptr *C.git_reference_iterator, r *Repository) *ReferenceIterator {
	return &ReferenceIterator{
		ptr:  ptr,
		repo: r,
	}
}

// Free the reference iterator
func (v *ReferenceIterator) Free() {
	runtime.SetFinalizer(v, nil)
	C.git_reference_iterator_free(v.ptr)
}

// ReferenceIsValidName ensures the reference name is well-formed.
//
// Valid reference names must follow one of two patterns:
//
// 1. Top-level names must contain only capital letters and underscores,
// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
//
// 2. Names prefixed with "refs/" can be almost anything. You must avoid
// the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences
// ".." and " @ {" which have special meaning to revparse.
func ReferenceIsValidName(name string) bool {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_reference_is_valid_name(cname) == 1 {
		return true
	}
	return false
}
<|endoftext|>


<commit_before><commit_msg>Move existing code into a function for consistency<commit_after><|endoftext|>


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

package descriptor

import (
	"fmt"
	"strings"

	"github.com/ligato/cn-infra/idxmap"

	"github.com/pkg/errors"

	"github.com/ligato/cn-infra/logging"
	l3 "github.com/ligato/vpp-agent/api/models/vpp/l3"
	kvs "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/descriptor/adapter"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vppcalls"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vrfidx"
)

const (
	// VrfTableDescriptorName is the name of the descriptor for VRF tables.
	VrfTableDescriptorName = "vpp-vrf-table"

	// how many characters a VRF table label is allowed to have
	//  - determined by much fits into the binary API (64 null-terminated character string)
	labelLengthLimit = 63
)

// A list of non-retriable errors:
var (
	// ErrVrfTableLabelTooLong is returned when VRF table label exceeds the length limit.
	ErrVrfTableLabelTooLong = errors.New("VPP VRF table label exceeds the length limit (63 characters)")
)

// VrfTableDescriptor teaches KVScheduler how to configure VPP VRF tables.
type VrfTableDescriptor struct {
	log       logging.Logger
	vtHandler vppcalls.VrfTableVppAPI
}

// NewVrfTableDescriptor creates a new instance of the VrfTable descriptor.
func NewVrfTableDescriptor(
	vtHandler vppcalls.VrfTableVppAPI, log logging.PluginLogger) *kvs.KVDescriptor {

	ctx := &VrfTableDescriptor{
		vtHandler: vtHandler,
		log:       log.NewLogger("vrf-table-descriptor"),
	}
	typedDescr := &adapter.VrfTableDescriptor{
		Name:               VrfTableDescriptorName,
		NBKeyPrefix:        l3.ModelVrfTable.KeyPrefix(),
		ValueTypeName:      l3.ModelVrfTable.ProtoName(),
		KeySelector:        l3.ModelVrfTable.IsKeyValid,
		KeyLabel:           l3.ModelVrfTable.StripKeyPrefix,
		WithMetadata:       true,
		MetadataMapFactory: ctx.MetadataFactory,
		ValueComparator:    ctx.EquivalentVrfTables,
		Validate:           ctx.Validate,
		Create:             ctx.Create,
		Delete:             ctx.Delete,
		Retrieve:           ctx.Retrieve,
	}
	return adapter.NewVrfTableDescriptor(typedDescr)
}

// EquivalentVrfTables is a comparison function for l3.VrfTable.
func (d *VrfTableDescriptor) EquivalentVrfTables(key string, oldVrfTable, newVrfTable *l3.VrfTable) bool {
	if getVrfTableLabel(oldVrfTable) != getVrfTableLabel(newVrfTable) {
		return false
	}
	return true
}

// MetadataFactory is a factory for index-map customized for VRFs.
func (d *VrfTableDescriptor) MetadataFactory() idxmap.NamedMappingRW {
	return vrfidx.NewVRFIndex(d.log, "vpp-vrf-index")
}

// Validate validates configuration of VPP VRF table.
func (d *VrfTableDescriptor) Validate(key string, vrfTable *l3.VrfTable) (err error) {
	if len(vrfTable.Label) > labelLengthLimit {
		return kvs.NewInvalidValueError(ErrVrfTableLabelTooLong, "label")
	}
	return nil
}

// Create adds VPP VRF table.
func (d *VrfTableDescriptor) Create(key string, vrfTable *l3.VrfTable) (metadata *vrfidx.VRFMetadata, err error) {
	if vrfTable.Id == 0 {
		// nothing to do, automatically created by VPP
	}
	err = d.vtHandler.AddVrfTable(vrfTable)
	if err != nil {
		return nil, err
	}

	// fill the metadata
	metadata = &vrfidx.VRFMetadata{
		Index:    vrfTable.Id,
		Protocol: vrfTable.Protocol,
	}

	return metadata, nil
}

// Delete removes VPP VRF table.
func (d *VrfTableDescriptor) Delete(key string, vrfTable *l3.VrfTable, metadata *vrfidx.VRFMetadata) error {
	if vrfTable.Id == 0 {
		// nothing to do, VRF ID=0 always exists
	}
	err := d.vtHandler.DelVrfTable(vrfTable)
	if err != nil {
		return err
	}

	return nil
}

// Retrieve returns all configured VPP VRF tables.
func (d *VrfTableDescriptor) Retrieve(correlate []adapter.VrfTableKVWithMetadata) (
	retrieved []adapter.VrfTableKVWithMetadata, err error,
) {
	tables, err := d.vtHandler.DumpVrfTables()
	if err != nil {
		return nil, errors.Errorf("failed to dump VPP VRF tables: %v", err)
	}

	for _, table := range tables {
		origin := kvs.UnknownOrigin
		if table.Id > 0 {
			origin = kvs.FromNB
		}
		retrieved = append(retrieved, adapter.VrfTableKVWithMetadata{
			Key:   l3.VrfTableKey(table.Id, table.Protocol),
			Value: table,
			Metadata: &vrfidx.VRFMetadata{
				Index:    table.Id,
				Protocol: table.Protocol,
			},
			Origin: origin,
		})
	}

	return retrieved, nil
}

func getVrfTableLabel(vrfTable *l3.VrfTable) string {
	if vrfTable.Label == "" {
		// label generated by VPP (e.g. "ipv4-VRF:0")
		return fmt.Sprintf("%s-VRF:%d",
			strings.ToLower(vrfTable.Protocol.String()), vrfTable.Id)
	}
	return vrfTable.Label
}
<commit_msg>fix: VRF table origin for SR policy (fixes #1566) (#1578)<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package descriptor

import (
	"fmt"
	"math"
	"strings"

	"github.com/ligato/cn-infra/idxmap"

	"github.com/pkg/errors"

	"github.com/ligato/cn-infra/logging"
	l3 "github.com/ligato/vpp-agent/api/models/vpp/l3"
	kvs "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/descriptor/adapter"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vppcalls"
	"github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vrfidx"
)

const (
	// VrfTableDescriptorName is the name of the descriptor for VRF tables.
	VrfTableDescriptorName = "vpp-vrf-table"

	// how many characters a VRF table label is allowed to have
	//  - determined by much fits into the binary API (64 null-terminated character string)
	labelLengthLimit = 63
)

// A list of non-retriable errors:
var (
	// ErrVrfTableLabelTooLong is returned when VRF table label exceeds the length limit.
	ErrVrfTableLabelTooLong = errors.New("VPP VRF table label exceeds the length limit (63 characters)")
)

// VrfTableDescriptor teaches KVScheduler how to configure VPP VRF tables.
type VrfTableDescriptor struct {
	log       logging.Logger
	vtHandler vppcalls.VrfTableVppAPI
}

// NewVrfTableDescriptor creates a new instance of the VrfTable descriptor.
func NewVrfTableDescriptor(
	vtHandler vppcalls.VrfTableVppAPI, log logging.PluginLogger) *kvs.KVDescriptor {

	ctx := &VrfTableDescriptor{
		vtHandler: vtHandler,
		log:       log.NewLogger("vrf-table-descriptor"),
	}
	typedDescr := &adapter.VrfTableDescriptor{
		Name:               VrfTableDescriptorName,
		NBKeyPrefix:        l3.ModelVrfTable.KeyPrefix(),
		ValueTypeName:      l3.ModelVrfTable.ProtoName(),
		KeySelector:        l3.ModelVrfTable.IsKeyValid,
		KeyLabel:           l3.ModelVrfTable.StripKeyPrefix,
		WithMetadata:       true,
		MetadataMapFactory: ctx.MetadataFactory,
		ValueComparator:    ctx.EquivalentVrfTables,
		Validate:           ctx.Validate,
		Create:             ctx.Create,
		Delete:             ctx.Delete,
		Retrieve:           ctx.Retrieve,
	}
	return adapter.NewVrfTableDescriptor(typedDescr)
}

// EquivalentVrfTables is a comparison function for l3.VrfTable.
func (d *VrfTableDescriptor) EquivalentVrfTables(key string, oldVrfTable, newVrfTable *l3.VrfTable) bool {
	if getVrfTableLabel(oldVrfTable) != getVrfTableLabel(newVrfTable) {
		return false
	}
	return true
}

// MetadataFactory is a factory for index-map customized for VRFs.
func (d *VrfTableDescriptor) MetadataFactory() idxmap.NamedMappingRW {
	return vrfidx.NewVRFIndex(d.log, "vpp-vrf-index")
}

// Validate validates configuration of VPP VRF table.
func (d *VrfTableDescriptor) Validate(key string, vrfTable *l3.VrfTable) (err error) {
	if len(vrfTable.Label) > labelLengthLimit {
		return kvs.NewInvalidValueError(ErrVrfTableLabelTooLong, "label")
	}
	return nil
}

// Create adds VPP VRF table.
func (d *VrfTableDescriptor) Create(key string, vrfTable *l3.VrfTable) (metadata *vrfidx.VRFMetadata, err error) {
	if vrfTable.Id == 0 {
		// nothing to do, automatically created by VPP
	}
	err = d.vtHandler.AddVrfTable(vrfTable)
	if err != nil {
		return nil, err
	}

	// fill the metadata
	metadata = &vrfidx.VRFMetadata{
		Index:    vrfTable.Id,
		Protocol: vrfTable.Protocol,
	}

	return metadata, nil
}

// Delete removes VPP VRF table.
func (d *VrfTableDescriptor) Delete(key string, vrfTable *l3.VrfTable, metadata *vrfidx.VRFMetadata) error {
	if vrfTable.Id == 0 {
		// nothing to do, VRF ID=0 always exists
	}
	err := d.vtHandler.DelVrfTable(vrfTable)
	if err != nil {
		return err
	}

	return nil
}

// Retrieve returns all configured VPP VRF tables.
func (d *VrfTableDescriptor) Retrieve(correlate []adapter.VrfTableKVWithMetadata) (
	retrieved []adapter.VrfTableKVWithMetadata, err error,
) {
	tables, err := d.vtHandler.DumpVrfTables()
	if err != nil {
		return nil, errors.Errorf("failed to dump VPP VRF tables: %v", err)
	}

	for _, table := range tables {
		origin := kvs.UnknownOrigin
		// VRF with ID=0 and ID=MaxUint32 are special
		// and should not be removed automatically
		if table.Id > 0 && table.Id < math.MaxUint32 {
			origin = kvs.FromNB
		}
		retrieved = append(retrieved, adapter.VrfTableKVWithMetadata{
			Key:   l3.VrfTableKey(table.Id, table.Protocol),
			Value: table,
			Metadata: &vrfidx.VRFMetadata{
				Index:    table.Id,
				Protocol: table.Protocol,
			},
			Origin: origin,
		})
	}

	return retrieved, nil
}

func getVrfTableLabel(vrfTable *l3.VrfTable) string {
	if vrfTable.Label == "" {
		// label generated by VPP (e.g. "ipv4-VRF:0")
		return fmt.Sprintf("%s-VRF:%d",
			strings.ToLower(vrfTable.Protocol.String()), vrfTable.Id)
	}
	return vrfTable.Label
}
<|endoftext|>


<commit_before><commit_msg>adding the rest of the supplied tests<commit_after><|endoftext|>


<commit_before><commit_msg>Ensure we can resolve a discovered Named WebSocket DNS-SD service to its original service name<commit_after><|endoftext|>


<commit_before><commit_msg>Preallocate space in new maps in resp.Any<commit_after><|endoftext|>


<commit_before>package widget

import (
	"net/http"

	"github.com/qor/admin"
	"github.com/qor/responder"
	"github.com/qor/serializable_meta"
)

type widgetController struct {
	Widgets *Widgets
}

func (wc widgetController) Index(context *admin.Context) {
	context = context.NewResourceContext(wc.Widgets.WidgetSettingResource)
	result, _, err := wc.getWidget(context)
	context.AddError(err)

	if context.HasError() {
		http.NotFound(context.Writer, context.Request)
	} else {
		responder.With("html", func() {
			context.Execute("index", result)
		}).With("json", func() {
			context.JSON("index", result)
		}).Respond(context.Request)
	}
}

func (wc widgetController) Edit(context *admin.Context) {
	context.Resource = wc.Widgets.WidgetSettingResource
	widgetSetting, scopes, err := wc.getWidget(context)
	context.AddError(err)
	context.Execute("edit", map[string]interface{}{"Scopes": scopes, "Widget": widgetSetting})
}

func (wc widgetController) Update(context *admin.Context) {
	context.Resource = wc.Widgets.WidgetSettingResource
	widgetSetting, scopes, err := wc.getWidget(context)
	context.AddError(err)

	if context.AddError(context.Resource.Decode(context.Context, widgetSetting)); !context.HasError() {
		context.AddError(context.Resource.CallSave(widgetSetting, context.Context))
		context.Execute("edit", map[string]interface{}{"Scopes": scopes, "Widget": widgetSetting})
		return
	}

	http.Redirect(context.Writer, context.Request, context.Request.URL.Path, http.StatusFound)
}

func (wc widgetController) InlineEdit(context *admin.Context) {
	context.Writer.Write([]byte(context.Render("inline_edit")))
}

func (wc widgetController) getWidget(context *admin.Context) (interface{}, []string, error) {
	if context.ResourceID == "" {
		// index page
		context.SetDB(context.GetDB().Where("scope = ?", "default"))
		results, err := context.FindMany()
		return results, []string{}, err
	}

	// show page
	result := wc.Widgets.WidgetSettingResource.NewStruct()
	scope := context.Request.URL.Query().Get("widget_scope")

	var scopes []string
	context.GetDB().Model(result).Where("name = ?", context.ResourceID).Pluck("scope", &scopes)

	var hasScope bool

	for _, s := range scopes {
		if scope == s {
			hasScope = true
			break
		}
	}

	if !hasScope {
		scope = "default"
	}

	err := context.GetDB().First(result, "name = ? AND scope = ?", context.ResourceID, scope).Error

	if widgetType := context.Request.URL.Query().Get("widget_type"); widgetType != "" {
		if serializableMeta, ok := result.(serializable_meta.SerializableMetaInterface); ok {
			serializableMeta.SetSerializableArgumentKind(widgetType)
			serializableMeta.SetSerializableArgumentValue(nil)
		}
	}
	return result, scopes, err
}
<commit_msg>Return happened errors when update widget<commit_after>package widget

import (
	"net/http"

	"github.com/qor/admin"
	"github.com/qor/responder"
	"github.com/qor/serializable_meta"
)

type widgetController struct {
	Widgets *Widgets
}

func (wc widgetController) Index(context *admin.Context) {
	context = context.NewResourceContext(wc.Widgets.WidgetSettingResource)
	result, _, err := wc.getWidget(context)
	context.AddError(err)

	if context.HasError() {
		http.NotFound(context.Writer, context.Request)
	} else {
		responder.With("html", func() {
			context.Execute("index", result)
		}).With("json", func() {
			context.JSON("index", result)
		}).Respond(context.Request)
	}
}

func (wc widgetController) Edit(context *admin.Context) {
	context.Resource = wc.Widgets.WidgetSettingResource
	widgetSetting, scopes, err := wc.getWidget(context)
	context.AddError(err)
	context.Execute("edit", map[string]interface{}{"Scopes": scopes, "Widget": widgetSetting})
}

func (wc widgetController) Update(context *admin.Context) {
	context.Resource = wc.Widgets.WidgetSettingResource
	widgetSetting, scopes, err := wc.getWidget(context)
	context.AddError(err)

	if context.AddError(context.Resource.Decode(context.Context, widgetSetting)); !context.HasError() {
		context.AddError(context.Resource.CallSave(widgetSetting, context.Context))
	}

	if context.HasError() {
		context.Execute("edit", map[string]interface{}{"Scopes": scopes, "Widget": widgetSetting})
	} else {
		http.Redirect(context.Writer, context.Request, context.Request.URL.Path, http.StatusFound)
	}
}

func (wc widgetController) InlineEdit(context *admin.Context) {
	context.Writer.Write([]byte(context.Render("inline_edit")))
}

func (wc widgetController) getWidget(context *admin.Context) (interface{}, []string, error) {
	if context.ResourceID == "" {
		// index page
		context.SetDB(context.GetDB().Where("scope = ?", "default"))
		results, err := context.FindMany()
		return results, []string{}, err
	}

	// show page
	result := wc.Widgets.WidgetSettingResource.NewStruct()
	scope := context.Request.URL.Query().Get("widget_scope")

	var scopes []string
	context.GetDB().Model(result).Where("name = ?", context.ResourceID).Pluck("scope", &scopes)

	var hasScope bool

	for _, s := range scopes {
		if scope == s {
			hasScope = true
			break
		}
	}

	if !hasScope {
		scope = "default"
	}

	err := context.GetDB().First(result, "name = ? AND scope = ?", context.ResourceID, scope).Error

	if widgetType := context.Request.URL.Query().Get("widget_type"); widgetType != "" {
		if serializableMeta, ok := result.(serializable_meta.SerializableMetaInterface); ok {
			serializableMeta.SetSerializableArgumentKind(widgetType)
			serializableMeta.SetSerializableArgumentValue(nil)
		}
	}
	return result, scopes, err
}
<|endoftext|>