<commit_before>package mqtt

import (
	"os"
	"testing"

	paho "github.com/eclipse/paho.mqtt.golang"
	"github.com/golang/protobuf/proto"
	"github.com/gomodule/redigo/redis"
	log "github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"github.com/brocaar/loraserver/api/gw"
	"github.com/brocaar/loraserver/internal/backend"
	"github.com/brocaar/loraserver/internal/backend/gateway/marshaler"
	"github.com/brocaar/loraserver/internal/common"
	"github.com/brocaar/lorawan"
)

type BackendTestSuite struct {
	suite.Suite

	backend    backend.Gateway
	redisPool  *redis.Pool
	mqttClient paho.Client
}

func (ts *BackendTestSuite) SetupSuite() {
	assert := require.New(ts.T())
	log.SetLevel(log.ErrorLevel)

	redisURL := "redis://localhost:6379/1"
	if v := os.Getenv("TEST_REDIS_URL"); v != "" {
		redisURL = v
	}

	ts.redisPool = common.NewRedisPool(redisURL, 10, 0)

	mqttServer := "tcp://127.0.0.1:1883/1"
	var mqttUsername string
	var mqttPassword string

	if v := os.Getenv("TEST__SERVER"); v != "" {
		mqttServer = v
	}
	if v := os.Getenv("TEST__USERNAME"); v != "" {
		mqttUsername = v
	}
	if v := os.Getenv("TEST__PASSWORD"); v != "" {
		mqttPassword = v
	}

	opts := paho.NewClientOptions().AddBroker(mqttServer).SetUsername(mqttUsername).SetPassword(mqttPassword)
	ts.mqttClient = paho.NewClient(opts)
	token := ts.mqttClient.Connect()
	token.Wait()
	assert.NoError(token.Error())

	var err error
	ts.backend, err = NewBackend(ts.redisPool, Config{
		Server:                mqttServer,
		Username:              mqttUsername,
		Password:              mqttPassword,
		CleanSession:          true,
		UplinkTopicTemplate:   "gateway/+/rx",
		DownlinkTopicTemplate: "gateway/{{ .MAC }}/tx",
		StatsTopicTemplate:    "gateway/+/stats",
		AckTopicTemplate:      "gateway/+/ack",
		ConfigTopicTemplate:   "gateway/{{ .MAC }}/config",
	})
	assert.NoError(err)

	ts.backend.(*Backend).setGatewayMarshaler(lorawan.EUI64{1, 2, 3, 4, 5, 6, 7, 8}, marshaler.Protobuf)
}

func (ts *BackendTestSuite) TearDownSuite() {
	assert := require.New(ts.T())

	assert.NoError(ts.backend.Close())
}

func (ts *BackendTestSuite) SetupTest() {
	MustFlushRedis(ts.redisPool)
}

func (ts *BackendTestSuite) TestUplinkFrame() {
	assert := require.New(ts.T())

	uplinkFrame := gw.UplinkFrame{
		PhyPayload: []byte{1, 2, 3, 4},
		TxInfo: &gw.UplinkTXInfo{
			Frequency: 868100000,
		},
		RxInfo: &gw.UplinkRXInfo{
			GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		},
	}

	b, err := proto.Marshal(&uplinkFrame)
	assert.NoError(err)
	uplinkFrame.XXX_sizecache = 0
	uplinkFrame.TxInfo.XXX_sizecache = 0
	uplinkFrame.RxInfo.XXX_sizecache = 0

	token := ts.mqttClient.Publish("gateway/0102030405060708/rx", 0, false, b)
	token.Wait()
	assert.NoError(token.Error())

	receivedUplink := <-ts.backend.RXPacketChan()
	assert.EqualValues(uplinkFrame, receivedUplink)
}

func (ts *BackendTestSuite) TestGatewayStats() {
	assert := require.New(ts.T())

	gatewayStats := gw.GatewayStats{
		GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
	}

	b, err := proto.Marshal(&gatewayStats)
	assert.NoError(err)
	gatewayStats.XXX_sizecache = 0

	token := ts.mqttClient.Publish("gateway/0102030405060708/stats", 0, false, b)
	token.Wait()
	assert.NoError(token.Error())

	receivedStats := <-ts.backend.StatsPacketChan()
	assert.EqualValues(gatewayStats, receivedStats)
}

func (ts *BackendTestSuite) TestDownlinkTXAck() {
	assert := require.New(ts.T())

	downlinkTXAck := gw.DownlinkTXAck{
		GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
	}
	b, err := proto.Marshal(&downlinkTXAck)
	assert.NoError(err)

	token := ts.mqttClient.Publish("gateway/0102030405060708/ack", 0, false, b)
	token.Wait()
	assert.NoError(token.Error())

	receivedAck := <-ts.backend.DownlinkTXAckChan()
	if !proto.Equal(&downlinkTXAck, &receivedAck) {
		assert.Equal(downlinkTXAck, receivedAck)
	}
}

func (ts *BackendTestSuite) TestSendDownlinkFrame() {
	assert := require.New(ts.T())

	downlinkFrameChan := make(chan gw.DownlinkFrame)
	token := ts.mqttClient.Subscribe("gateway/+/tx", 0, func(c paho.Client, msg paho.Message) {
		var pl gw.DownlinkFrame
		if err := proto.Unmarshal(msg.Payload(), &pl); err != nil {
			panic(err)
		}

		downlinkFrameChan <- pl
	})
	token.Wait()
	assert.NoError(token.Error())

	downlinkFrame := gw.DownlinkFrame{
		PhyPayload: []byte{1, 2, 3, 4},
		TxInfo: &gw.DownlinkTXInfo{
			GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		},
	}
	assert.NoError(ts.backend.SendTXPacket(downlinkFrame))
	downlinkFrame.TxInfo.XXX_sizecache = 0
	downlinkFrame.XXX_sizecache = 0

	downlinkReceived := <-downlinkFrameChan
	assert.EqualValues(downlinkFrame, downlinkReceived)
}

func (ts *BackendTestSuite) TestSendGatewayConfiguration() {
	assert := require.New(ts.T())

	gatewayConfigChan := make(chan gw.GatewayConfiguration)
	token := ts.mqttClient.Subscribe("gateway/+/config", 0, func(c paho.Client, msg paho.Message) {
		var pl gw.GatewayConfiguration
		if err := proto.Unmarshal(msg.Payload(), &pl); err != nil {
			panic(err)
		}
		gatewayConfigChan <- pl
	})
	token.Wait()
	assert.NoError(token.Error())

	gatewayConfig := gw.GatewayConfiguration{
		GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		Version:   "1.2.3",
	}
	assert.NoError(ts.backend.SendGatewayConfigPacket(gatewayConfig))
	gatewayConfig.XXX_sizecache = 0

	configReceived := <-gatewayConfigChan
	assert.Equal(gatewayConfig, configReceived)
}

func TestBackend(t *testing.T) {
	suite.Run(t, new(BackendTestSuite))
}
<commit_msg>Fix getting mqtt test server variable.<commit_after>package mqtt

import (
	"os"
	"testing"

	paho "github.com/eclipse/paho.mqtt.golang"
	"github.com/golang/protobuf/proto"
	"github.com/gomodule/redigo/redis"
	log "github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"github.com/brocaar/loraserver/api/gw"
	"github.com/brocaar/loraserver/internal/backend"
	"github.com/brocaar/loraserver/internal/backend/gateway/marshaler"
	"github.com/brocaar/loraserver/internal/common"
	"github.com/brocaar/lorawan"
)

type BackendTestSuite struct {
	suite.Suite

	backend    backend.Gateway
	redisPool  *redis.Pool
	mqttClient paho.Client
}

func (ts *BackendTestSuite) SetupSuite() {
	assert := require.New(ts.T())
	log.SetLevel(log.ErrorLevel)

	redisURL := "redis://localhost:6379/1"
	if v := os.Getenv("TEST_REDIS_URL"); v != "" {
		redisURL = v
	}

	ts.redisPool = common.NewRedisPool(redisURL, 10, 0)

	mqttServer := "tcp://127.0.0.1:1883/1"
	var mqttUsername string
	var mqttPassword string

	if v := os.Getenv("TEST_MQTT_SERVER"); v != "" {
		mqttServer = v
	}
	if v := os.Getenv("TEST_MQTT_USERNAME"); v != "" {
		mqttUsername = v
	}
	if v := os.Getenv("TEST_MQTT_PASSWORD"); v != "" {
		mqttPassword = v
	}

	opts := paho.NewClientOptions().AddBroker(mqttServer).SetUsername(mqttUsername).SetPassword(mqttPassword)
	ts.mqttClient = paho.NewClient(opts)
	token := ts.mqttClient.Connect()
	token.Wait()
	assert.NoError(token.Error())

	var err error
	ts.backend, err = NewBackend(ts.redisPool, Config{
		Server:                mqttServer,
		Username:              mqttUsername,
		Password:              mqttPassword,
		CleanSession:          true,
		UplinkTopicTemplate:   "gateway/+/rx",
		DownlinkTopicTemplate: "gateway/{{ .MAC }}/tx",
		StatsTopicTemplate:    "gateway/+/stats",
		AckTopicTemplate:      "gateway/+/ack",
		ConfigTopicTemplate:   "gateway/{{ .MAC }}/config",
	})
	assert.NoError(err)

	ts.backend.(*Backend).setGatewayMarshaler(lorawan.EUI64{1, 2, 3, 4, 5, 6, 7, 8}, marshaler.Protobuf)
}

func (ts *BackendTestSuite) TearDownSuite() {
	assert := require.New(ts.T())

	assert.NoError(ts.backend.Close())
}

func (ts *BackendTestSuite) SetupTest() {
	MustFlushRedis(ts.redisPool)
}

func (ts *BackendTestSuite) TestUplinkFrame() {
	assert := require.New(ts.T())

	uplinkFrame := gw.UplinkFrame{
		PhyPayload: []byte{1, 2, 3, 4},
		TxInfo: &gw.UplinkTXInfo{
			Frequency: 868100000,
		},
		RxInfo: &gw.UplinkRXInfo{
			GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		},
	}

	b, err := proto.Marshal(&uplinkFrame)
	assert.NoError(err)
	uplinkFrame.XXX_sizecache = 0
	uplinkFrame.TxInfo.XXX_sizecache = 0
	uplinkFrame.RxInfo.XXX_sizecache = 0

	token := ts.mqttClient.Publish("gateway/0102030405060708/rx", 0, false, b)
	token.Wait()
	assert.NoError(token.Error())

	receivedUplink := <-ts.backend.RXPacketChan()
	assert.EqualValues(uplinkFrame, receivedUplink)
}

func (ts *BackendTestSuite) TestGatewayStats() {
	assert := require.New(ts.T())

	gatewayStats := gw.GatewayStats{
		GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
	}

	b, err := proto.Marshal(&gatewayStats)
	assert.NoError(err)
	gatewayStats.XXX_sizecache = 0

	token := ts.mqttClient.Publish("gateway/0102030405060708/stats", 0, false, b)
	token.Wait()
	assert.NoError(token.Error())

	receivedStats := <-ts.backend.StatsPacketChan()
	assert.EqualValues(gatewayStats, receivedStats)
}

func (ts *BackendTestSuite) TestDownlinkTXAck() {
	assert := require.New(ts.T())

	downlinkTXAck := gw.DownlinkTXAck{
		GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
	}
	b, err := proto.Marshal(&downlinkTXAck)
	assert.NoError(err)

	token := ts.mqttClient.Publish("gateway/0102030405060708/ack", 0, false, b)
	token.Wait()
	assert.NoError(token.Error())

	receivedAck := <-ts.backend.DownlinkTXAckChan()
	if !proto.Equal(&downlinkTXAck, &receivedAck) {
		assert.Equal(downlinkTXAck, receivedAck)
	}
}

func (ts *BackendTestSuite) TestSendDownlinkFrame() {
	assert := require.New(ts.T())

	downlinkFrameChan := make(chan gw.DownlinkFrame)
	token := ts.mqttClient.Subscribe("gateway/+/tx", 0, func(c paho.Client, msg paho.Message) {
		var pl gw.DownlinkFrame
		if err := proto.Unmarshal(msg.Payload(), &pl); err != nil {
			panic(err)
		}

		downlinkFrameChan <- pl
	})
	token.Wait()
	assert.NoError(token.Error())

	downlinkFrame := gw.DownlinkFrame{
		PhyPayload: []byte{1, 2, 3, 4},
		TxInfo: &gw.DownlinkTXInfo{
			GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		},
	}
	assert.NoError(ts.backend.SendTXPacket(downlinkFrame))
	downlinkFrame.TxInfo.XXX_sizecache = 0
	downlinkFrame.XXX_sizecache = 0

	downlinkReceived := <-downlinkFrameChan
	assert.EqualValues(downlinkFrame, downlinkReceived)
}

func (ts *BackendTestSuite) TestSendGatewayConfiguration() {
	assert := require.New(ts.T())

	gatewayConfigChan := make(chan gw.GatewayConfiguration)
	token := ts.mqttClient.Subscribe("gateway/+/config", 0, func(c paho.Client, msg paho.Message) {
		var pl gw.GatewayConfiguration
		if err := proto.Unmarshal(msg.Payload(), &pl); err != nil {
			panic(err)
		}
		gatewayConfigChan <- pl
	})
	token.Wait()
	assert.NoError(token.Error())

	gatewayConfig := gw.GatewayConfiguration{
		GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
		Version:   "1.2.3",
	}
	assert.NoError(ts.backend.SendGatewayConfigPacket(gatewayConfig))
	gatewayConfig.XXX_sizecache = 0

	configReceived := <-gatewayConfigChan
	assert.Equal(gatewayConfig, configReceived)
}

func TestBackend(t *testing.T) {
	suite.Run(t, new(BackendTestSuite))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/Stantheman/youtube-gif-go/api"
	"github.com/Stantheman/youtube-gif-go/config"
	"github.com/Stantheman/youtube-gif-go/logger"
	"github.com/Stantheman/youtube-gif-go/redisPool"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
	"github.com/unrolled/render"
	"net/http"
	"os"
	"strconv"
)

// http://codegangsta.gitbooks.io/building-web-apps-with-go/

var l = logger.Get()
var rend = render.New(render.Options{})

func main() {
	conf := config.Get()

	r := mux.NewRouter().StrictSlash(true)
	r.HandleFunc("/", HomeHandler)

	// Gifs collection
	gifs := r.Path("/gifs").Subrouter()
	gifs.Methods("GET").HandlerFunc(GifsIndexHandler)
	gifs.Methods("POST").HandlerFunc(GifsCreateHandler)

	// Gifs singular
	gif := r.PathPrefix(`/gifs/{id:\d+}`).Subrouter()
	gif.Methods("GET").HandlerFunc(GifShowHandler)

	connect := conf.Site.Ip + ":" + conf.Site.Port
	fmt.Println("Starting server on " + connect)
	http.ListenAndServe(connect, r)
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(rw, "Home")
}

// GifsIndexHandler: ask redis for a list of completed images
func GifsIndexHandler(rw http.ResponseWriter, r *http.Request) {

	c := redisPool.Pool.Get()
	defer c.Close()

	// cant tell if active image list is weird or redis
	if exists, _ := redis.Int(c.Do("EXISTS", "active_images")); exists != 1 {
		rend.JSON(rw, http.StatusNoContent, jsonErr(errors.New("no images")))
		l.Notice("checking active_images: none yet")
		return
	}

	images, err := redis.Strings(c.Do("LRANGE", "active_images", 0, -1))
	if err != nil {
		rend.JSON(rw, http.StatusInternalServerError, jsonErr(err))
		l.Notice("getting images: " + err.Error())
		return
	}

	jlist := make(map[string]string)
	for _, image := range images {
		jlist[image] = "/gifs/" + image
	}
	rend.JSON(rw, http.StatusOK, jlist)
}

/* GifsCreateHandler: take a URL over post and put it in a queue
Pass URL to this handler, it'll check that it looks like a URL
give a 202 ACK, and bounce*/
func GifsCreateHandler(rw http.ResponseWriter, r *http.Request) {

	if err := r.ParseForm(); err != nil {
		l.Notice("parsing form: " + err.Error())
		rend.JSON(rw, http.StatusBadRequest, jsonErr(err))
		return
	}

	msg, errs := api.ValidateParams(r.Form)
	if len(errs) > 0 {
		var probs []string = make([]string, 0)
		for _, prob := range errs {
			probs = append(probs, prob.Error())
		}
		rend.JSON(rw, http.StatusBadRequest, map[string][]string{"errors": probs})
		return
	}

	id, err := addURLToRedis(msg)
	if err != nil {
		rend.JSON(rw, http.StatusInternalServerError, jsonErr(err))
		return
	}
	// validate options, pass 202 Accepted
	rend.JSON(rw, http.StatusAccepted, map[string]string{"id": id})
}

// GifShowHandler: do we even want to hand back gifs? json?
func GifShowHandler(rw http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]

	c := redisPool.Pool.Get()
	defer c.Close()

	// freak if it ain't real
	status, err := redis.String(c.Do("HGET", "gif:"+id, "status"))
	if err != nil {
		rend.JSON(rw, http.StatusBadRequest, jsonErr(errors.New("gif doesn't exist")))
		l.Notice("getting gif: " + err.Error())
		return
	}

	// if it's not real yet, tell them
	if status != "available" {
		rend.JSON(rw, http.StatusOK, map[string]string{"status": status})
		return
	}

	// verify that the file is actually there
	conf := config.Get()
	path := conf.Site.Gif_Dir + "/" + id + ".gif"
	if _, err := os.Stat(path); os.IsNotExist(err) {
		rend.JSON(rw, http.StatusInternalServerError, jsonErr(errors.New("file doesn't exist")))
		l.Err("checking on disk path: " + err.Error())
		return
	}

	http.ServeFile(rw, r, path)
}

func addURLToRedis(msg *api.PubSubMessage) (id string, err error) {

	c := redisPool.Pool.Get()
	defer c.Close()

	// replace id increment with GUID?
	new_id, err := redis.Int(c.Do("INCR", "autoincr:gif"))
	if err != nil {
		l.Err("getting new id: " + err.Error())
		return "", err
	}
	msg.ID = strconv.Itoa(new_id)
	keyname := "gif:" + msg.ID

	// set up the json bomb for pubsub. grab pubsubmessage from worker
	payload, err := json.Marshal(msg)
	if err != nil {
		l.Err("marshalling json: " + err.Error())
		return "", err
	}

	// pipelined transaction city
	c.Send("MULTI")
	c.Send("HSET", keyname, "origin", msg.URL)
	// enum/smarts for status/queue
	c.Send("HSET", keyname, "status", "pending download")
	// one shot, do not miss your chance to blow
	c.Send("EXPIRE", keyname, 3600)
	c.Send("PUBLISH", "download-queue", payload)
	if _, err = c.Do("EXEC"); err != nil {
		l.Err("adding image to redis: " + err.Error())
		return "", err
	}

	return msg.ID, nil
}

func jsonErr(msg error) (res map[string]string) {
	return map[string]string{
		"error": msg.Error(),
	}
}
<commit_msg>lazy cors response, should be middleware<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/Stantheman/youtube-gif-go/api"
	"github.com/Stantheman/youtube-gif-go/config"
	"github.com/Stantheman/youtube-gif-go/logger"
	"github.com/Stantheman/youtube-gif-go/redisPool"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
	"github.com/unrolled/render"
	"net/http"
	"os"
	"strconv"
)

// http://codegangsta.gitbooks.io/building-web-apps-with-go/

var l = logger.Get()
var rend = render.New(render.Options{})

func main() {
	conf := config.Get()

	r := mux.NewRouter().StrictSlash(true)
	r.HandleFunc("/", HomeHandler)

	// Gifs collection
	gifs := r.Path("/gifs").Subrouter()
	gifs.Methods("GET").HandlerFunc(GifsIndexHandler)
	gifs.Methods("POST").HandlerFunc(GifsCreateHandler)

	// Gifs singular
	gif := r.PathPrefix(`/gifs/{id:\d+}`).Subrouter()
	gif.Methods("GET").HandlerFunc(GifShowHandler)

	connect := conf.Site.Ip + ":" + conf.Site.Port
	fmt.Println("Starting server on " + connect)
	http.ListenAndServe(connect, r)
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(rw, "Home")
}

// GifsIndexHandler: ask redis for a list of completed images
func GifsIndexHandler(rw http.ResponseWriter, r *http.Request) {

	c := redisPool.Pool.Get()
	defer c.Close()

	// cant tell if active image list is weird or redis
	if exists, _ := redis.Int(c.Do("EXISTS", "active_images")); exists != 1 {
		rend.JSON(rw, http.StatusNoContent, jsonErr(errors.New("no images")))
		l.Notice("checking active_images: none yet")
		return
	}

	images, err := redis.Strings(c.Do("LRANGE", "active_images", 0, -1))
	if err != nil {
		rend.JSON(rw, http.StatusInternalServerError, jsonErr(err))
		l.Notice("getting images: " + err.Error())
		return
	}

	jlist := make(map[string]string)
	for _, image := range images {
		jlist[image] = "/gifs/" + image
	}
	rend.JSON(rw, http.StatusOK, jlist)
}

/* GifsCreateHandler: take a URL over post and put it in a queue
Pass URL to this handler, it'll check that it looks like a URL
give a 202 ACK, and bounce*/
func GifsCreateHandler(rw http.ResponseWriter, r *http.Request) {

	if err := r.ParseForm(); err != nil {
		l.Notice("parsing form: " + err.Error())
		rend.JSON(rw, http.StatusBadRequest, jsonErr(err))
		return
	}

	msg, errs := api.ValidateParams(r.Form)
	if len(errs) > 0 {
		var probs []string = make([]string, 0)
		for _, prob := range errs {
			probs = append(probs, prob.Error())
		}
		rend.JSON(rw, http.StatusBadRequest, map[string][]string{"errors": probs})
		return
	}

	id, err := addURLToRedis(msg)
	if err != nil {
		rend.JSON(rw, http.StatusInternalServerError, jsonErr(err))
		return
	}
	// validate options, pass 202 Accepted
	rw.Header().Set("Location", "/gifs/"+id)
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	rend.JSON(rw, http.StatusAccepted, map[string]string{"id": id})
}

// GifShowHandler: do we even want to hand back gifs? json?
func GifShowHandler(rw http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]

	c := redisPool.Pool.Get()
	defer c.Close()

	rw.Header().Set("Access-Control-Allow-Origin", "*")

	// freak if it ain't real
	status, err := redis.String(c.Do("HGET", "gif:"+id, "status"))
	if err != nil {
		rend.JSON(rw, http.StatusBadRequest, jsonErr(errors.New("gif doesn't exist")))
		l.Notice("getting gif: " + err.Error())
		return
	}

	// if it's not real yet, tell them
	if status != "available" {
		rend.JSON(rw, http.StatusOK, map[string]string{"status": status})
		return
	}

	// verify that the file is actually there
	conf := config.Get()
	path := conf.Site.Gif_Dir + "/" + id + ".gif"
	if _, err := os.Stat(path); os.IsNotExist(err) {
		rend.JSON(rw, http.StatusInternalServerError, jsonErr(errors.New("file doesn't exist")))
		l.Err("checking on disk path: " + err.Error())
		return
	}

	http.ServeFile(rw, r, path)
}

func addURLToRedis(msg *api.PubSubMessage) (id string, err error) {

	c := redisPool.Pool.Get()
	defer c.Close()

	// replace id increment with GUID?
	new_id, err := redis.Int(c.Do("INCR", "autoincr:gif"))
	if err != nil {
		l.Err("getting new id: " + err.Error())
		return "", err
	}
	msg.ID = strconv.Itoa(new_id)
	keyname := "gif:" + msg.ID

	// set up the json bomb for pubsub. grab pubsubmessage from worker
	payload, err := json.Marshal(msg)
	if err != nil {
		l.Err("marshalling json: " + err.Error())
		return "", err
	}

	// pipelined transaction city
	c.Send("MULTI")
	c.Send("HSET", keyname, "origin", msg.URL)
	// enum/smarts for status/queue
	c.Send("HSET", keyname, "status", "pending download")
	// one shot, do not miss your chance to blow
	c.Send("EXPIRE", keyname, 3600)
	c.Send("PUBLISH", "download-queue", payload)
	if _, err = c.Do("EXEC"); err != nil {
		l.Err("adding image to redis: " + err.Error())
		return "", err
	}

	return msg.ID, nil
}

func jsonErr(msg error) (res map[string]string) {
	return map[string]string{
		"error": msg.Error(),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os/user"
	"path/filepath"

	. "github.com/kkdai/youtube"
)

func main() {
	flag.Parse()
	log.Println(flag.Args())
	usr, _ := user.Current()
	currentDir := fmt.Sprintf("%v/Movies/youtubedr", usr.HomeDir)
	log.Println("download to dir=", currentDir)
	y := NewYoutube(true)
	arg := flag.Arg(0)
	if err := y.DecodeURL(arg); err != nil {
		fmt.Println("err:", err)
	}
	if err := y.StartDownload(filepath.Join(currentDir, "dl.mp4")); err != nil {
		fmt.Println("err:", err)
	}
}
<commit_msg>Fix cannot download video cause crash.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os/user"
	"path/filepath"

	. "github.com/kkdai/youtube"
)

func main() {
	flag.Parse()
	log.Println(flag.Args())
	usr, _ := user.Current()
	currentDir := fmt.Sprintf("%v/Movies/youtubedr", usr.HomeDir)
	log.Println("download to dir=", currentDir)
	y := NewYoutube(true)
	arg := flag.Arg(0)
	if err := y.DecodeURL(arg); err != nil {
		fmt.Println("err:", err)
		return
	}
	if err := y.StartDownload(filepath.Join(currentDir, "dl.mp4")); err != nil {
		fmt.Println("err:", err)
	}
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/plans"
	"github.com/hashicorp/terraform/plans/objchange"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/tfdiags"
)

// evalReadData implements shared methods and data for the individual  data
// source eval nodes.
type evalReadData struct {
	Addr           addrs.ResourceInstance
	Config         *configs.Resource
	Provider       *providers.Interface
	ProviderAddr   addrs.AbsProviderConfig
	ProviderMetas  map[addrs.Provider]*configs.ProviderMeta
	ProviderSchema **ProviderSchema

	// Planned is set when dealing with data resources that were deferred to
	// the apply walk, to let us see what was planned. If this is set, the
	// evaluation of the config is required to produce a wholly-known
	// configuration which is consistent with the partial object included
	// in this planned change.
	Planned **plans.ResourceInstanceChange

	// State is the current state for the data source, and is updated once the
	// new state has been read.
	// While data sources are read-only, we need to start with the prior state
	// to determine if we have a change or not.  If we needed to read a new
	// value, but it still matches the previous state, then we can record a
	// NoNop change. If the states don't match then we record a Read change so
	// that the new value is applied to the state.
	State **states.ResourceInstanceObject

	// Output change records any change for this data source, which is
	// interpreted differently than changes for managed resources.
	// - During Refresh, this change is only used to correctly evaluate
	// references to the data source, but it is not saved.
	// - If a planned change has the action of plans.Read, it indicates that the
	// data source could not be evaluated yet, and reading is being deferred to
	// apply.
	// - If planned action is plans.Update, it indicates that the data source
	// was read, and the result needs to be stored in state during apply.
	OutputChange **plans.ResourceInstanceChange

	// dependsOn stores the list of transitive resource addresses that any
	// configuration depends_on references may resolve to. This is used to
	// determine if there are any changes that will force this data sources to
	// be deferred to apply.
	dependsOn []addrs.ConfigResource
	// forceDependsOn indicates that resources may be missing from dependsOn,
	// but the parent module may have depends_on configured.
	forceDependsOn bool
}

// readDataSource handles everything needed to call ReadDataSource on the provider.
// A previously evaluated configVal can be passed in, or a new one is generated
// from the resource configuration.
func (n *evalReadData) readDataSource(ctx EvalContext, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	var newVal cty.Value

	config := *n.Config
	absAddr := n.Addr.Absolute(ctx.Path())

	if n.ProviderSchema == nil || *n.ProviderSchema == nil {
		diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
		return newVal, diags
	}

	provider := *n.Provider

	providerSchema := *n.ProviderSchema
	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
	if schema == nil {
		// Should be caught during validation, so we don't bother with a pretty error here
		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type))
		return newVal, diags
	}

	metaConfigVal, metaDiags := n.providerMetas(ctx)
	diags = diags.Append(metaDiags)
	if diags.HasErrors() {
		return newVal, diags
	}

	log.Printf("[TRACE] EvalReadData: Re-validating config for %s", absAddr)
	validateResp := provider.ValidateDataSourceConfig(
		providers.ValidateDataSourceConfigRequest{
			TypeName: n.Addr.Resource.Type,
			Config:   configVal,
		},
	)
	if validateResp.Diagnostics.HasErrors() {
		return newVal, validateResp.Diagnostics.InConfigBody(config.Config)
	}

	// If we get down here then our configuration is complete and we're read
	// to actually call the provider to read the data.
	log.Printf("[TRACE] EvalReadData: %s configuration is complete, so reading from provider", absAddr)

	resp := provider.ReadDataSource(providers.ReadDataSourceRequest{
		TypeName:     n.Addr.Resource.Type,
		Config:       configVal,
		ProviderMeta: metaConfigVal,
	})
	diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config))
	if diags.HasErrors() {
		return newVal, diags
	}
	newVal = resp.State
	if newVal == cty.NilVal {
		// This can happen with incompletely-configured mocks. We'll allow it
		// and treat it as an alias for a properly-typed null value.
		newVal = cty.NullVal(schema.ImpliedType())
	}

	for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Provider produced invalid object",
			fmt.Sprintf(
				"Provider %q produced an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
				n.ProviderAddr.Provider.String(), tfdiags.FormatErrorPrefixed(err, absAddr.String()),
			),
		))
	}
	if diags.HasErrors() {
		return newVal, diags
	}

	if newVal.IsNull() {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Provider produced null object",
			fmt.Sprintf(
				"Provider %q produced a null value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
				n.ProviderAddr.Provider.String(), absAddr,
			),
		))
	}

	if !newVal.IsNull() && !newVal.IsWhollyKnown() {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Provider produced invalid object",
			fmt.Sprintf(
				"Provider %q produced a value for %s that is not wholly known.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
				n.ProviderAddr.Provider.String(), absAddr,
			),
		))

		// We'll still save the object, but we need to eliminate any unknown
		// values first because we can't serialize them in the state file.
		// Note that this may cause set elements to be coalesced if they
		// differed only by having unknown values, but we don't worry about
		// that here because we're saving the value only for inspection
		// purposes; the error we added above will halt the graph walk.
		newVal = cty.UnknownAsNull(newVal)
	}

	return newVal, diags
}

func (n *evalReadData) providerMetas(ctx EvalContext) (cty.Value, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
	if n.ProviderMetas != nil {
		if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil {
			// if the provider doesn't support this feature, throw an error
			if (*n.ProviderSchema).ProviderMeta == nil {
				diags = diags.Append(&hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
					Detail:   fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
					Subject:  &m.ProviderRange,
				})
			} else {
				var configDiags tfdiags.Diagnostics
				metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
				diags = diags.Append(configDiags)
			}
		}
	}
	return metaConfigVal, diags
}

// evalReadDataRefresh is an EvalNode implementation that handled the data
// resource lifecycle during refresh
type evalReadDataRefresh struct {
	evalReadData
}

func (n *evalReadDataRefresh) Eval(ctx EvalContext) (interface{}, error) {
	var diags tfdiags.Diagnostics

	if n.ProviderSchema == nil || *n.ProviderSchema == nil {
		return nil, fmt.Errorf("provider schema not available for %s", n.Addr)
	}

	absAddr := n.Addr.Absolute(ctx.Path())
	config := *n.Config
	providerSchema := *n.ProviderSchema
	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
	if schema == nil {
		// Should be caught during validation, so we don't bother with a pretty error here
		return nil, fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type)
	}

	objTy := schema.ImpliedType()
	priorVal := cty.NullVal(objTy)
	if n.State != nil && *n.State != nil {
		priorVal = (*n.State).Value
	}

	forEach, _ := evaluateForEachExpression(config.ForEach, ctx)
	keyData := EvalDataForInstanceKey(n.Addr.Key, forEach)

	configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
	diags = diags.Append(configDiags)
	if configDiags.HasErrors() {
		return nil, diags.ErrWithWarnings()
	}

	configKnown := configVal.IsWhollyKnown()
	// If our configuration contains any unknown values, then we must defer the
	// read until plan or apply. If we've never read this data source and we
	// have any depends_on, we will have to defer reading until plan to resolve
	// the dependency changes.
	// Assuming we can read the data source with depends_on if we have
	// existing state is a compromise to prevent data sources from continually
	// showing a diff. We have to make the assumption that if we have a prior
	// state, since there are no prior dependency changes happening during
	// refresh, that we can read this resource. If there are dependency updates
	// in the config, they we be discovered in plan and the data source will be
	// read again.
	if !configKnown || (priorVal.IsNull() && n.forcePlanRead()) {
		if configKnown {
			log.Printf("[TRACE] evalReadDataRefresh: %s configuration is fully known, but we're forcing a read plan to be created", absAddr)
		} else {
			log.Printf("[TRACE] evalReadDataRefresh: %s configuration not fully known yet, so deferring to apply phase", absAddr)
		}

		// We need to store a change so tat other references to this data
		// source can resolve correctly, since the state is not going to be up
		// to date.
		*n.OutputChange = &plans.ResourceInstanceChange{
			Addr:         absAddr,
			ProviderAddr: n.ProviderAddr,
			Change: plans.Change{
				Action: plans.Read,
				Before: priorVal,
				After:  objchange.PlannedDataResourceObject(schema, configVal),
			},
		}

		*n.State = &states.ResourceInstanceObject{
			Value:  cty.NullVal(objTy),
			Status: states.ObjectPlanned,
		}

		return nil, diags.ErrWithWarnings()
	}

	if err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreRefresh(absAddr, states.CurrentGen, priorVal)
	}); err != nil {
		diags = diags.Append(err)
		return nil, diags.ErrWithWarnings()
	}

	newVal, readDiags := n.readDataSource(ctx, configVal)
	diags = diags.Append(readDiags)
	if diags.HasErrors() {
		return nil, diags.ErrWithWarnings()
	}

	// This may still have been refreshed with references to resources that
	// will be updated, but that will be caught as a change during plan.
	*n.State = &states.ResourceInstanceObject{
		Value:  newVal,
		Status: states.ObjectReady,
	}

	if err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostRefresh(absAddr, states.CurrentGen, priorVal, newVal)
	}); err != nil {
		diags = diags.Append(err)
	}

	return nil, diags.ErrWithWarnings()
}

// forcePlanRead determines if we need to override the usual behavior of
// immediately reading from the data source where possible, instead forcing us
// to generate a plan.
func (n *evalReadDataRefresh) forcePlanRead() bool {
	return len(n.Config.DependsOn) > 0 || len(n.dependsOn) > 0 || n.forceDependsOn
}
<commit_msg>remove unused refresh node<commit_after>package terraform

import (
	"fmt"
	"log"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/plans"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/tfdiags"
)

// evalReadData implements shared methods and data for the individual  data
// source eval nodes.
type evalReadData struct {
	Addr           addrs.ResourceInstance
	Config         *configs.Resource
	Provider       *providers.Interface
	ProviderAddr   addrs.AbsProviderConfig
	ProviderMetas  map[addrs.Provider]*configs.ProviderMeta
	ProviderSchema **ProviderSchema

	// Planned is set when dealing with data resources that were deferred to
	// the apply walk, to let us see what was planned. If this is set, the
	// evaluation of the config is required to produce a wholly-known
	// configuration which is consistent with the partial object included
	// in this planned change.
	Planned **plans.ResourceInstanceChange

	// State is the current state for the data source, and is updated once the
	// new state has been read.
	// While data sources are read-only, we need to start with the prior state
	// to determine if we have a change or not.  If we needed to read a new
	// value, but it still matches the previous state, then we can record a
	// NoNop change. If the states don't match then we record a Read change so
	// that the new value is applied to the state.
	State **states.ResourceInstanceObject

	// Output change records any change for this data source, which is
	// interpreted differently than changes for managed resources.
	// - During Refresh, this change is only used to correctly evaluate
	// references to the data source, but it is not saved.
	// - If a planned change has the action of plans.Read, it indicates that the
	// data source could not be evaluated yet, and reading is being deferred to
	// apply.
	// - If planned action is plans.Update, it indicates that the data source
	// was read, and the result needs to be stored in state during apply.
	OutputChange **plans.ResourceInstanceChange

	// dependsOn stores the list of transitive resource addresses that any
	// configuration depends_on references may resolve to. This is used to
	// determine if there are any changes that will force this data sources to
	// be deferred to apply.
	dependsOn []addrs.ConfigResource
	// forceDependsOn indicates that resources may be missing from dependsOn,
	// but the parent module may have depends_on configured.
	forceDependsOn bool
}

// readDataSource handles everything needed to call ReadDataSource on the provider.
// A previously evaluated configVal can be passed in, or a new one is generated
// from the resource configuration.
func (n *evalReadData) readDataSource(ctx EvalContext, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	var newVal cty.Value

	config := *n.Config
	absAddr := n.Addr.Absolute(ctx.Path())

	if n.ProviderSchema == nil || *n.ProviderSchema == nil {
		diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
		return newVal, diags
	}

	provider := *n.Provider

	providerSchema := *n.ProviderSchema
	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
	if schema == nil {
		// Should be caught during validation, so we don't bother with a pretty error here
		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type))
		return newVal, diags
	}

	metaConfigVal, metaDiags := n.providerMetas(ctx)
	diags = diags.Append(metaDiags)
	if diags.HasErrors() {
		return newVal, diags
	}

	log.Printf("[TRACE] EvalReadData: Re-validating config for %s", absAddr)
	validateResp := provider.ValidateDataSourceConfig(
		providers.ValidateDataSourceConfigRequest{
			TypeName: n.Addr.Resource.Type,
			Config:   configVal,
		},
	)
	if validateResp.Diagnostics.HasErrors() {
		return newVal, validateResp.Diagnostics.InConfigBody(config.Config)
	}

	// If we get down here then our configuration is complete and we're read
	// to actually call the provider to read the data.
	log.Printf("[TRACE] EvalReadData: %s configuration is complete, so reading from provider", absAddr)

	resp := provider.ReadDataSource(providers.ReadDataSourceRequest{
		TypeName:     n.Addr.Resource.Type,
		Config:       configVal,
		ProviderMeta: metaConfigVal,
	})
	diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config))
	if diags.HasErrors() {
		return newVal, diags
	}
	newVal = resp.State
	if newVal == cty.NilVal {
		// This can happen with incompletely-configured mocks. We'll allow it
		// and treat it as an alias for a properly-typed null value.
		newVal = cty.NullVal(schema.ImpliedType())
	}

	for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Provider produced invalid object",
			fmt.Sprintf(
				"Provider %q produced an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
				n.ProviderAddr.Provider.String(), tfdiags.FormatErrorPrefixed(err, absAddr.String()),
			),
		))
	}
	if diags.HasErrors() {
		return newVal, diags
	}

	if newVal.IsNull() {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Provider produced null object",
			fmt.Sprintf(
				"Provider %q produced a null value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
				n.ProviderAddr.Provider.String(), absAddr,
			),
		))
	}

	if !newVal.IsNull() && !newVal.IsWhollyKnown() {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Provider produced invalid object",
			fmt.Sprintf(
				"Provider %q produced a value for %s that is not wholly known.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
				n.ProviderAddr.Provider.String(), absAddr,
			),
		))

		// We'll still save the object, but we need to eliminate any unknown
		// values first because we can't serialize them in the state file.
		// Note that this may cause set elements to be coalesced if they
		// differed only by having unknown values, but we don't worry about
		// that here because we're saving the value only for inspection
		// purposes; the error we added above will halt the graph walk.
		newVal = cty.UnknownAsNull(newVal)
	}

	return newVal, diags
}

func (n *evalReadData) providerMetas(ctx EvalContext) (cty.Value, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
	if n.ProviderMetas != nil {
		if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil {
			// if the provider doesn't support this feature, throw an error
			if (*n.ProviderSchema).ProviderMeta == nil {
				diags = diags.Append(&hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
					Detail:   fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
					Subject:  &m.ProviderRange,
				})
			} else {
				var configDiags tfdiags.Diagnostics
				metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
				diags = diags.Append(configDiags)
			}
		}
	}
	return metaConfigVal, diags
}
<|endoftext|>


<commit_before>package pay

import (
	"errors"
	"crypto/md5"
	"encoding/xml"
	"fmt"
	"strings"
	"github.com/silenceper/wechat/context"
	"github.com/silenceper/wechat/util"
)

var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"

// Pay struct extends context
type Pay struct {
	*context.Context
}

// 传入的参数，用于生成 prepay_id 的必需参数
// PayParams was NEEDED when request unifiedorder
type PayParams struct {
	TotalFee    string
	CreateIP    string
	Body        string
	OutTradeNo  string
	OpenID      string
}

// PayConfig 是传出用于 jsdk 用的参数
type PayConfig struct {
	Timestamp       int64
	NonceStr        string
	PrePayID        string
	SignType        string
	Sign            string
}

// payResult 是 unifie order 接口的返回
type payResult struct {
	ReturnCode      string `xml:"return_code"`
	ReturnMsg       string `xml:"return_msg"`
	AppID           string `xml:"appid,omitempty"`
	MchID           string `xml:"mch_id,omitempty"`
	NonceStr        string `xml:"nonce_str,omitempty"`
	Sign            string `xml:"sign,omitempty"`
	ResultCode      string `xml:"result_code,omitempty"`
	TradeType       string `xml:"trade_type,omitempty"`
	PrePayID        string `xml:"prepay_id,omitempty"`
	CodeURL         string `xml:"code_url,omitempty"`
	ErrCode         string `xml:"err_code,omitempty"`
	ErrCodeDes      string `xml:"err_code_des,omitempty"`
}

//payRequest 接口请求参数
type payRequest struct {
	AppID           string `xml:"appid"`
	MchID           string `xml:"mch_id"`
	NotifyUrl       string `xml:"notify_url"` //通知地址
	DeviceInfo      string `xml:"device_info,omitempty"`
	NonceStr        string `xml:"nonce_str"`
	Sign            string `xml:"sign"`
	SignType        string `xml:"sign_type,omitempty"`
	Body            string `xml:"body"`
	Detail          string `xml:"detail,omitempty"`
	Attach          string `xml:"attach,omitempty"` //附加数据
	OutTradeNo      string `xml:"out_trade_no"` //商户订单号
	FeeType         string `xml:"fee_type,omitempty"` //标价币种
	TotalFee        string `xml:"total_fee"` //标价金额
	SpbillCreateIp  string `xml:"spbill_create_ip"` //终端IP
	TimeStart       string `xml:"time_start,omitempty"`  //交易起始时间
	TimeExpire      string `xml:"time_expire,omitempty"`  //交易结束时间
	GoodsTag        string `xml:"goods_tag,omitempty"`  //订单优惠标记
	TradeType       string `xml:"trade_type"` //交易类型
	ProductId       string `xml:"product_id,omitempty"`  //商品ID
	LimitPay        string `xml:"limit_pay,omitempty"` //
	OpenID          string `xml:"openid,omitempty"` //用户标识
	SceneInfo       string `xml:"scene_info,omitempty"` //场景信息
}

// NewPay return an instance of Pay package
func NewPay(ctx *context.Context) *Pay {
	pay := Pay{Context: ctx}
	return &pay
}

// PrePayId will request wechat merchant api and request for a pre payment order id
func (pcf *Pay) PrePayId(p *PayParams) (prePayID string, err error) {
	nonceStr := util.RandomStr(32)
	tradeType := "JSAPI"
	template := "appid=%s&body=%s&mch_id=%s&nonce_str=%s&notify_url=%s&out_trade_no=%s&spbill_create_ip=%s&total_fee=%s&trade_type=%s"
	str := fmt.Sprintf(template, pcf.AppID, p.Body, pcf.PayMchID, nonceStr, pcf.PayNotifyURL, p.OutTradeNo, p.CreateIP, p.TotalFee, tradeType)
	str += "&key="pcf.PayKey
	sum := md5.Sum([]byte(str))
	signature := string(sum[:])
	sign := strings.ToUpper(signature)
	request := payRequest{
		AppID: pcf.AppID,
		MchID: pcf.PayMchID,
		NotifyUrl: pcf.PayNotifyURL,
		NonceStr: nonceStr,
		Sign: sign,
		Body: p.Body,
		OutTradeNo: p.OutTradeNo,
		TotalFee: p.TotalFee,
		SpbillCreateIp: p.CreateIP,
		OpenID: p.OpenID,
		TradeType: tradeType,
	}
	rawRet, err := util.PostXML(payGateway, request)
	if err != nil {
		return "", errors.New(err.Error() + " parameters : " + str)
	}
	payRet := payResult{}
	err = xml.Unmarshal(rawRet, &payRet)
	if err != nil {
		return "", errors.New(err.Error())
	}
	if payRet.ReturnCode == "SUCCESS" {
		//pay success
		if payRet.ResultCode == "SUCCESS" {
			return payRet.PrePayID, nil
		}
		return "", errors.New(payRet.ErrCode + payRet.ErrCodeDes)
	} else {
		return "", errors.New("xml unmarshal err : raw - " + string(rawRet))
	}
}<commit_msg>fix payment typo<commit_after>package pay

import (
	"errors"
	"crypto/md5"
	"encoding/xml"
	"fmt"
	"strings"
	"github.com/silenceper/wechat/context"
	"github.com/silenceper/wechat/util"
)

var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"

// Pay struct extends context
type Pay struct {
	*context.Context
}

// 传入的参数，用于生成 prepay_id 的必需参数
// PayParams was NEEDED when request unifiedorder
type PayParams struct {
	TotalFee    string
	CreateIP    string
	Body        string
	OutTradeNo  string
	OpenID      string
}

// PayConfig 是传出用于 jsdk 用的参数
type PayConfig struct {
	Timestamp       int64
	NonceStr        string
	PrePayID        string
	SignType        string
	Sign            string
}

// payResult 是 unifie order 接口的返回
type payResult struct {
	ReturnCode      string `xml:"return_code"`
	ReturnMsg       string `xml:"return_msg"`
	AppID           string `xml:"appid,omitempty"`
	MchID           string `xml:"mch_id,omitempty"`
	NonceStr        string `xml:"nonce_str,omitempty"`
	Sign            string `xml:"sign,omitempty"`
	ResultCode      string `xml:"result_code,omitempty"`
	TradeType       string `xml:"trade_type,omitempty"`
	PrePayID        string `xml:"prepay_id,omitempty"`
	CodeURL         string `xml:"code_url,omitempty"`
	ErrCode         string `xml:"err_code,omitempty"`
	ErrCodeDes      string `xml:"err_code_des,omitempty"`
}

//payRequest 接口请求参数
type payRequest struct {
	AppID           string `xml:"appid"`
	MchID           string `xml:"mch_id"`
	NotifyUrl       string `xml:"notify_url"` //通知地址
	DeviceInfo      string `xml:"device_info,omitempty"`
	NonceStr        string `xml:"nonce_str"`
	Sign            string `xml:"sign"`
	SignType        string `xml:"sign_type,omitempty"`
	Body            string `xml:"body"`
	Detail          string `xml:"detail,omitempty"`
	Attach          string `xml:"attach,omitempty"` //附加数据
	OutTradeNo      string `xml:"out_trade_no"` //商户订单号
	FeeType         string `xml:"fee_type,omitempty"` //标价币种
	TotalFee        string `xml:"total_fee"` //标价金额
	SpbillCreateIp  string `xml:"spbill_create_ip"` //终端IP
	TimeStart       string `xml:"time_start,omitempty"`  //交易起始时间
	TimeExpire      string `xml:"time_expire,omitempty"`  //交易结束时间
	GoodsTag        string `xml:"goods_tag,omitempty"`  //订单优惠标记
	TradeType       string `xml:"trade_type"` //交易类型
	ProductId       string `xml:"product_id,omitempty"`  //商品ID
	LimitPay        string `xml:"limit_pay,omitempty"` //
	OpenID          string `xml:"openid,omitempty"` //用户标识
	SceneInfo       string `xml:"scene_info,omitempty"` //场景信息
}

// NewPay return an instance of Pay package
func NewPay(ctx *context.Context) *Pay {
	pay := Pay{Context: ctx}
	return &pay
}

// PrePayId will request wechat merchant api and request for a pre payment order id
func (pcf *Pay) PrePayId(p *PayParams) (prePayID string, err error) {
	nonceStr := util.RandomStr(32)
	tradeType := "JSAPI"
	template := "appid=%s&body=%s&mch_id=%s&nonce_str=%s&notify_url=%s&out_trade_no=%s&spbill_create_ip=%s&total_fee=%s&trade_type=%s"
	str := fmt.Sprintf(template, pcf.AppID, p.Body, pcf.PayMchID, nonceStr, pcf.PayNotifyURL, p.OutTradeNo, p.CreateIP, p.TotalFee, tradeType)
	str := str + "&key=" + pcf.PayKey
	sum := md5.Sum([]byte(str))
	signature := string(sum[:])
	sign := strings.ToUpper(signature)
	request := payRequest{
		AppID: pcf.AppID,
		MchID: pcf.PayMchID,
		NotifyUrl: pcf.PayNotifyURL,
		NonceStr: nonceStr,
		Sign: sign,
		Body: p.Body,
		OutTradeNo: p.OutTradeNo,
		TotalFee: p.TotalFee,
		SpbillCreateIp: p.CreateIP,
		OpenID: p.OpenID,
		TradeType: tradeType,
	}
	rawRet, err := util.PostXML(payGateway, request)
	if err != nil {
		return "", errors.New(err.Error() + " parameters : " + str)
	}
	payRet := payResult{}
	err = xml.Unmarshal(rawRet, &payRet)
	if err != nil {
		return "", errors.New(err.Error())
	}
	if payRet.ReturnCode == "SUCCESS" {
		//pay success
		if payRet.ResultCode == "SUCCESS" {
			return payRet.PrePayID, nil
		}
		return "", errors.New(payRet.ErrCode + payRet.ErrCodeDes)
	} else {
		return "", errors.New("xml unmarshal err : raw - " + string(rawRet))
	}
}<|endoftext|>


<commit_before>package adyen

/**********
* Payment *
**********/

// One-click functionality gives the shopper the option to store their payment details with the merchant, within the Adyen environment.
//
// In this type of transaction, the shopper needs to enter the CVC code for the transaction to get through.
//
// Link: https://docs.adyen.com/developers/api-reference/payments-api#recurring
const (
	RecurringPaymentOneClick              = "ONECLICK"
	RecurringPaymentRecurring             = "RECURRING"
	ShopperInteractionContAuth            = "ContAuth"
	SelectRecurringDetailReferenceLatests = "LATEST"
)

// AuthoriseEncrypted structure for Authorisation request (with encrypted card information)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type AuthoriseEncrypted struct {
	AdditionalData                   *AdditionalData `json:"additionalData,omitempty"`
	Amount                           *Amount         `json:"amount"`
	BillingAddress                   *Address        `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address        `json:"deliveryAddress,omitempty"`
	Reference                        string          `json:"reference"`
	MerchantAccount                  string          `json:"merchantAccount"`
	ShopperReference                 string          `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring      `json:"recurring,omitempty"`
	ShopperEmail                     string          `json:"shopperEmail,omitempty"`
	ShopperInteraction               string          `json:"shopperInteraction,omitempty"`
	ShopperIP                        string          `json:"shopperIP,omitempty"`
	ShopperLocale                    string          `json:"shopperLocale,omitempty"`
	ShopperName                      *Name           `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string          `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo    `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                *int            `json:"captureDelayHours,omitempty"`
}

// Authorise structure for Authorisation request (card is not encrypted)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type Authorise struct {
	Card                             *Card        `json:"card,omitempty"`
	Amount                           *Amount      `json:"amount"`
	BillingAddress                   *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address     `json:"deliveryAddress,omitempty"`
	Reference                        string       `json:"reference"`
	MerchantAccount                  string       `json:"merchantAccount"`
	ShopperReference                 string       `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring   `json:"recurring,omitempty"`
	ShopperEmail                     string       `json:"shopperEmail,omitempty"`
	ShopperInteraction               string       `json:"shopperInteraction,omitempty"`
	ShopperIP                        string       `json:"shopperIP,omitempty"`
	ShopperLocale                    string       `json:"shopperLocale,omitempty"`
	ShopperName                      *Name        `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string       `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                *int         `json:"captureDelayHours,omitempty"`
}

// AuthoriseResponse is a response structure for Adyen
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentresult
type AuthoriseResponse struct {
	PspReference   string          `json:"pspReference"`
	ResultCode     string          `json:"resultCode"`
	AuthCode       string          `json:"authCode"`
	RefusalReason  string          `json:"refusalReason"`
	IssuerURL      string          `json:"issuerUrl"`
	MD             string          `json:"md"`
	PaRequest      string          `json:"paRequest"`
	AdditionalData *AdditionalData `json:"additionalData,omitempty"`
}

// AdditionalData stores encrypted information about customer's credit card
type AdditionalData struct {
	Content                           string      `json:"card.encrypted.json,omitempty"`
	AliasType                         string      `json:"aliasType,omitempty"`
	Alias                             string      `json:"alias,omitempty"`
	ExpiryDate                        string      `json:"expiryDate,omitempty"`
	CardBin                           string      `json:"cardBin,omitempty"`
	CardSummary                       string      `json:"cardSummary,omitempty"`
	PaymentMethod                     string      `json:"paymentMethod,omitempty"`
	CardPaymentMethod                 string      `json:"cardPaymentMethod,omitempty"`
	CardIssuingCountry                string      `json:"cardIssuingCountry,omitempty"`
	RecurringDetailReference          string      `json:"recurring.recurringDetailReference,omitempty"`
	ExecuteThreeD                     *StringBool `json:"executeThreeD,omitempty"`
	FundingSource                     string      `json:"fundingSource,omitempty"`
	RequestedTestAcquirerResponseCode int         `json:"RequestedTestAcquirerResponseCode,omitempty"` //Used for trigger error from adyen
}

// BrowserInfo hold information on the user browser
type BrowserInfo struct {
	AcceptHeader string `json:"acceptHeader"`
	UserAgent    string `json:"userAgent"`
}

// Recurring hold the behavior for a future payment : could be ONECLICK or RECURRING
type Recurring struct {
	Contract string `json:"contract"`
}

/*************
* Payment 3D *
*************/

// Authorise3D structure for Authorisation request (card is not encrypted)
//
// https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest3d
type Authorise3D struct {
	BillingAddress  *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress *Address     `json:"deliveryAddress,omitempty"`
	MD              string       `json:"md"`
	MerchantAccount string       `json:"merchantAccount"`
	BrowserInfo     *BrowserInfo `json:"browserInfo"`
	PaResponse      string       `json:"paResponse"`
	ShopperEmail    string       `json:"shopperEmail,omitempty"`
	ShopperIP       string       `json:"shopperIP,omitempty"`
	ShopperLocale   string       `json:"shopperLocale,omitempty"`
	ShopperName     *Name        `json:"shopperName,omitempty"`
}

/*******************
* Directory lookup *
*******************/

// DirectoryLookupRequest - get list of available payment methods based on skin, country and order details
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryrequest
// CountryCode could be used to test local payment methods, if client's IP is from different country
type DirectoryLookupRequest struct {
	CurrencyCode      string `url:"currencyCode"`
	MerchantAccount   string `url:"merchantAccount"`
	PaymentAmount     int    `url:"paymentAmount"`
	SkinCode          string `url:"skinCode"`
	MerchantReference string `url:"merchantReference"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
}

// DirectoryLookupResponse - api response for DirectoryLookupRequest
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryresponse
type DirectoryLookupResponse struct {
	PaymentMethods []PaymentMethod `json:"paymentMethods"`
}

// PaymentMethod - structure for single payment method in directory look up response
//
// Part of DirectoryLookupResponse
type PaymentMethod struct {
	BrandCode string   `json:"brandCode"`
	Name      string   `json:"name"`
	Logos     logos    `json:"logos"`
	Issuers   []issuer `json:"issuers"`
}

// logos - payment method logos
//
// Part of DirectoryLookupResponse
type logos struct {
	Normal string `json:"normal"`
	Small  string `json:"small"`
	Tiny   string `json:"tiny"`
}

// issuer - bank issuer type
//
// Part of DirectoryLookupResponse
type issuer struct {
	IssuerID string `json:"issuerId"`
	Name     string `json:"name"`
}

/***********
* Skip HPP *
***********/

// SkipHppRequest contains data that would be used to create Adyen HPP redirect URL
//
// Link: https://docs.adyen.com/developers/ecommerce-integration/local-payment-methods
//
// Request description: https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#skipdetailsrequest
type SkipHppRequest struct {
	MerchantReference string `url:"merchantReference"`
	PaymentAmount     int    `url:"paymentAmount"`
	CurrencyCode      string `url:"currencyCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
	SkinCode          string `url:"skinCode"`
	MerchantAccount   string `url:"merchantAccount"`
	ShopperLocale     string `url:"shopperLocale"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	BrandCode         string `url:"brandCode"`
	IssuerID          string `url:"issuerId"`
}
<commit_msg>Add card holder name<commit_after>package adyen

/**********
* Payment *
**********/

// One-click functionality gives the shopper the option to store their payment details with the merchant, within the Adyen environment.
//
// In this type of transaction, the shopper needs to enter the CVC code for the transaction to get through.
//
// Link: https://docs.adyen.com/developers/api-reference/payments-api#recurring
const (
	RecurringPaymentOneClick              = "ONECLICK"
	RecurringPaymentRecurring             = "RECURRING"
	ShopperInteractionContAuth            = "ContAuth"
	SelectRecurringDetailReferenceLatests = "LATEST"
)

// AuthoriseEncrypted structure for Authorisation request (with encrypted card information)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type AuthoriseEncrypted struct {
	AdditionalData                   *AdditionalData `json:"additionalData,omitempty"`
	Amount                           *Amount         `json:"amount"`
	BillingAddress                   *Address        `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address        `json:"deliveryAddress,omitempty"`
	Reference                        string          `json:"reference"`
	MerchantAccount                  string          `json:"merchantAccount"`
	ShopperReference                 string          `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring      `json:"recurring,omitempty"`
	ShopperEmail                     string          `json:"shopperEmail,omitempty"`
	ShopperInteraction               string          `json:"shopperInteraction,omitempty"`
	ShopperIP                        string          `json:"shopperIP,omitempty"`
	ShopperLocale                    string          `json:"shopperLocale,omitempty"`
	ShopperName                      *Name           `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string          `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo    `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                *int            `json:"captureDelayHours,omitempty"`
}

// Authorise structure for Authorisation request (card is not encrypted)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type Authorise struct {
	Card                             *Card        `json:"card,omitempty"`
	Amount                           *Amount      `json:"amount"`
	BillingAddress                   *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address     `json:"deliveryAddress,omitempty"`
	Reference                        string       `json:"reference"`
	MerchantAccount                  string       `json:"merchantAccount"`
	ShopperReference                 string       `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring   `json:"recurring,omitempty"`
	ShopperEmail                     string       `json:"shopperEmail,omitempty"`
	ShopperInteraction               string       `json:"shopperInteraction,omitempty"`
	ShopperIP                        string       `json:"shopperIP,omitempty"`
	ShopperLocale                    string       `json:"shopperLocale,omitempty"`
	ShopperName                      *Name        `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string       `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                *int         `json:"captureDelayHours,omitempty"`
}

// AuthoriseResponse is a response structure for Adyen
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentresult
type AuthoriseResponse struct {
	PspReference   string          `json:"pspReference"`
	ResultCode     string          `json:"resultCode"`
	AuthCode       string          `json:"authCode"`
	RefusalReason  string          `json:"refusalReason"`
	IssuerURL      string          `json:"issuerUrl"`
	MD             string          `json:"md"`
	PaRequest      string          `json:"paRequest"`
	AdditionalData *AdditionalData `json:"additionalData,omitempty"`
}

// AdditionalData stores encrypted information about customer's credit card
type AdditionalData struct {
	Content                           string      `json:"card.encrypted.json,omitempty"`
	AliasType                         string      `json:"aliasType,omitempty"`
	Alias                             string      `json:"alias,omitempty"`
	ExpiryDate                        string      `json:"expiryDate,omitempty"`
	CardBin                           string      `json:"cardBin,omitempty"`
	CardSummary                       string      `json:"cardSummary,omitempty"`
	CardHolderName                    string      `json:"cardHolderName,omitempty"`
	PaymentMethod                     string      `json:"paymentMethod,omitempty"`
	CardPaymentMethod                 string      `json:"cardPaymentMethod,omitempty"`
	CardIssuingCountry                string      `json:"cardIssuingCountry,omitempty"`
	RecurringDetailReference          string      `json:"recurring.recurringDetailReference,omitempty"`
	ExecuteThreeD                     *StringBool `json:"executeThreeD,omitempty"`
	FundingSource                     string      `json:"fundingSource,omitempty"`
	RequestedTestAcquirerResponseCode int         `json:"RequestedTestAcquirerResponseCode,omitempty"` //Used for trigger error from adyen
}

// BrowserInfo hold information on the user browser
type BrowserInfo struct {
	AcceptHeader string `json:"acceptHeader"`
	UserAgent    string `json:"userAgent"`
}

// Recurring hold the behavior for a future payment : could be ONECLICK or RECURRING
type Recurring struct {
	Contract string `json:"contract"`
}

/*************
* Payment 3D *
*************/

// Authorise3D structure for Authorisation request (card is not encrypted)
//
// https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest3d
type Authorise3D struct {
	BillingAddress  *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress *Address     `json:"deliveryAddress,omitempty"`
	MD              string       `json:"md"`
	MerchantAccount string       `json:"merchantAccount"`
	BrowserInfo     *BrowserInfo `json:"browserInfo"`
	PaResponse      string       `json:"paResponse"`
	ShopperEmail    string       `json:"shopperEmail,omitempty"`
	ShopperIP       string       `json:"shopperIP,omitempty"`
	ShopperLocale   string       `json:"shopperLocale,omitempty"`
	ShopperName     *Name        `json:"shopperName,omitempty"`
}

/*******************
* Directory lookup *
*******************/

// DirectoryLookupRequest - get list of available payment methods based on skin, country and order details
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryrequest
// CountryCode could be used to test local payment methods, if client's IP is from different country
type DirectoryLookupRequest struct {
	CurrencyCode      string `url:"currencyCode"`
	MerchantAccount   string `url:"merchantAccount"`
	PaymentAmount     int    `url:"paymentAmount"`
	SkinCode          string `url:"skinCode"`
	MerchantReference string `url:"merchantReference"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
}

// DirectoryLookupResponse - api response for DirectoryLookupRequest
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryresponse
type DirectoryLookupResponse struct {
	PaymentMethods []PaymentMethod `json:"paymentMethods"`
}

// PaymentMethod - structure for single payment method in directory look up response
//
// Part of DirectoryLookupResponse
type PaymentMethod struct {
	BrandCode string   `json:"brandCode"`
	Name      string   `json:"name"`
	Logos     logos    `json:"logos"`
	Issuers   []issuer `json:"issuers"`
}

// logos - payment method logos
//
// Part of DirectoryLookupResponse
type logos struct {
	Normal string `json:"normal"`
	Small  string `json:"small"`
	Tiny   string `json:"tiny"`
}

// issuer - bank issuer type
//
// Part of DirectoryLookupResponse
type issuer struct {
	IssuerID string `json:"issuerId"`
	Name     string `json:"name"`
}

/***********
* Skip HPP *
***********/

// SkipHppRequest contains data that would be used to create Adyen HPP redirect URL
//
// Link: https://docs.adyen.com/developers/ecommerce-integration/local-payment-methods
//
// Request description: https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#skipdetailsrequest
type SkipHppRequest struct {
	MerchantReference string `url:"merchantReference"`
	PaymentAmount     int    `url:"paymentAmount"`
	CurrencyCode      string `url:"currencyCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
	SkinCode          string `url:"skinCode"`
	MerchantAccount   string `url:"merchantAccount"`
	ShopperLocale     string `url:"shopperLocale"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	BrandCode         string `url:"brandCode"`
	IssuerID          string `url:"issuerId"`
}
<|endoftext|>


<commit_before>package gocardless_pro_go

type PaymentService service

type Payment struct {
	Amount 			int64       		`json:"amount, omitempty"`
	AmountRefunded	int64				`json:"amount_refunded,omitempty"`
	ChargeDate		string				`json:"charge_date,omitempty"`
	CreatedAt		string				`json:"created_at,omitempty"`
	Description 	string				`json:"description,omitempty"`
	ID				string				`json:"id,omitempty"`
	Reference		string				`json:"reference,omitempty"`
	Status			Status				`json:"status,omitempty"`
	Currency		Currency			`json:"currency,omitempty"`
	Links			[]PaymentLink		`json:"links,omitempty"`
	Metadata		map[string]string	`json:"metadata,omitempty"`
}

type  Currency	struct {
	Currency 	string		`json:"currency,omitempty"`
}

<commit_msg>Create Payment in payment service<commit_after>package gocardless_pro_go

import "fmt"

type PaymentService service

type Payment struct {
	Amount 			int64       		`json:"amount, omitempty"`
	AmountRefunded	int64				`json:"amount_refunded,omitempty"`
	ChargeDate		string				`json:"charge_date,omitempty"`
	CreatedAt		string				`json:"created_at,omitempty"`
	Description 	string				`json:"description,omitempty"`
	ID				string				`json:"id,omitempty"`
	Reference		string				`json:"reference,omitempty"`
	Status			Status				`json:"status,omitempty"`
	Currency		Currency			`json:"currency,omitempty"`
	Links			map[string]string	`json:"links,omitempty"`
	Metadata		map[string]string	`json:"metadata,omitempty"`
}

type  Currency	struct {
	Currency 	string		`json:"currency,omitempty"`
}

type PaymentListRequest struct {
	CreatedAt 			CreatedAt	`json:"created_at,omitempty"`
	Limit				int			`json:"limit,omitempty"`
	Before				string		`json:"before,omitempty"`
	After				string		`json:"after,omitempty"`
	Creditor			string		`json:"creditor,omitempty"`
	Customer			string		`json:"customer,omitempty"`
	CustomerBankAccount string		`json:"customer_bank_account,omitempty"`
	Reference			string		`json:"reference,omitempty"`
	Status				string		`json:"status,omitempty"`
}

type PaymentList struct {
	Meta   ListMeta
	Values []CustomerBankAccount `json:"data"`
}

type PaymentCreateRequest struct {
	Metadata			map[string]string		`json:"metadata,omitempty"`
	Reference			string					`json:"reference,omitempty"`
	Scheme				string					`json:"scheme,omitempty"`
	AccountNumber		string					`json:"account_number,omitempty"`
	Links				[]string				`json:"links,omitempty"`
}

type PaymentCancelRequest struct {
	Identity 	string  	`json:"identity,omitempty"`
}

// Create creates a new payment
func (s *PaymentService) CreatePayment(paymentReq *PaymentCreateRequest) (*Payment, error) {
	u := fmt.Sprintf("/payments")
	payment := &Payment{}
	err := s.client.Call("POST", u, paymentReq, payment)

	return payment, err
}

// List returns a list of mandates
func (s *MandateService) ListPayments(req *MandateListRequest) (*MandateList, error) {
	return s.ListNMandates(10, 0, req)
}

func (s *MandateService) ListNMandates(count, offset int, req *MandateListRequest) (*MandateList, error) {
	params := url.Values{}
	params.Add("after", req.After)
	params.Add("before", req.Before)
	params.Add("created_at[gt]", req.CreatedAt.Gt)
	params.Add("created_at[gte]", req.CreatedAt.Gte)
	params.Add("created_at[lt]", req.CreatedAt.Lt)
	params.Add("created_at[lte]", req.CreatedAt.Lte)
	params.Add("limit", string(req.Limit))
	params.Add("reference", req.Reference)
	params.Add("status", req.Status)
	params.Add("customer_bank_account", req.CustomerBankAccount)
	params.Add("customer", req.Customer)
	params.Add("creditor", req.Creditor)
	u := paginateURL("/mandates", count, offset)
	mandates := &MandateList{}
	err := s.client.Call("GET", u, params, mandates)

	return mandates, err
}

func (s *MandateService) GetMandate(id string) (*Mandate, error) {
	u := fmt.Sprintf("/mandates/%s", id)
	mandate := &Mandate{}
	err := s.client.Call("GET", u, nil, mandate)

	return mandate, err
}

func (s *MandateService) UpdateMandate(updatedMandate *Mandate, metadata map[string]string) (*Mandate, error) {
	params := url.Values{}
	params.Add("metadata", string(metadata))
	u := fmt.Sprintf("mandates/%d", updatedMandate.ID)
	mandate := &Mandate{}
	err := s.client.Call("PUT", u, params, mandate)

	return mandate, err
}

func (s *MandateService) CancelMandate(mandateToCancel *Mandate, metadata map[string]string) (*Response, error) {
	params := url.Values{}
	params.Add("metadata", string(metadata))
	u := fmt.Sprintf("/mandates/%s/actions/cancel", mandateToCancel.ID)
	resp := &Response{}
	err := s.client.Call("POST", u, params, resp)

	return resp, err
}

func (s *MandateService) ReinstateMandate(mandateToReinstate *Mandate, metadata map[string]string) (*Response, error) {
	params := url.Values{}
	params.Add("metadata", string(metadata))
	u := fmt.Sprintf("/mandates/%s/actions/reinstate", mandateToReinstate.ID)
	resp := &Response{}
	err := s.client.Call("POST", u, params, resp)

	return resp, err
}

<|endoftext|>


<commit_before>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
import "C"

// MediaList represents a collection of media files.
type MediaList struct {
	list *C.libvlc_media_list_t
}

// NewMediaList creates an empty media list.
func NewMediaList() (*MediaList, error) {
	if inst == nil {
		return nil, ErrModuleNotInitialized
	}

	var list *C.libvlc_media_list_t
	if list = C.libvlc_media_list_new(inst.handle); list == nil {
		return nil, getError()
	}

	return &MediaList{list: list}, nil
}

// Release destroys the media list instance.
func (ml *MediaList) Release() error {
	if ml.list == nil {
		return nil
	}

	C.libvlc_media_list_release(ml.list)
	ml.list = nil

	return getError()
}

// AddMedia adds a Media instance to the media list.
func (ml *MediaList) AddMedia(m *Media) error {
	if ml.list == nil {
		return ErrMediaListNotInitialized
	}
	if m == nil || m.media == nil {
		return ErrMediaNotInitialized
	}

	C.libvlc_media_list_add_media(ml.list, m.media)
	return getError()
}

// AddMediaFromPath loads a media file from path and adds it
// to the the media list.
func (ml *MediaList) AddMediaFromPath(path string) error {
	media, err := NewMediaFromPath(path)
	if err != nil {
		return err
	}

	return ml.AddMedia(media)
}

// AddMediaFromURL loads a media file from url and adds it
// to the the media list.
func (ml *MediaList) AddMediaFromURL(url string) error {
	media, err := NewMediaFromURL(url)
	if err != nil {
		return err
	}

	return ml.AddMedia(media)
}

// Lock makes the caller the current owner of the media list.
func (ml *MediaList) Lock() error {
	if ml.list == nil {
		return ErrMediaListNotInitialized
	}

	C.libvlc_media_list_lock(ml.list)
	return getError()
}

// Unlock releases ownership of the media list.
func (ml *MediaList) Unlock() error {
	if ml.list == nil {
		return ErrMediaListNotInitialized
	}

	C.libvlc_media_list_unlock(ml.list)
	return getError()
}

// EventManager returns the event manager responsible for the media list.
func (ml *MediaList) EventManager() (*EventManager, error) {
	if ml.list == nil {
		return nil, ErrMediaListNotInitialized
	}

	manager := C.libvlc_media_list_event_manager(ml.list)
	if manager == nil {
		return nil, ErrMissingEventManager
	}

	return newEventManager(manager), nil
}
<commit_msg>Lock media list when adding new items<commit_after>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
import "C"

// MediaList represents a collection of media files.
type MediaList struct {
	list *C.libvlc_media_list_t
}

// NewMediaList creates an empty media list.
func NewMediaList() (*MediaList, error) {
	if inst == nil {
		return nil, ErrModuleNotInitialized
	}

	var list *C.libvlc_media_list_t
	if list = C.libvlc_media_list_new(inst.handle); list == nil {
		return nil, getError()
	}

	return &MediaList{list: list}, nil
}

// Release destroys the media list instance.
func (ml *MediaList) Release() error {
	if ml.list == nil {
		return nil
	}

	C.libvlc_media_list_release(ml.list)
	ml.list = nil

	return getError()
}

// AddMedia adds a Media instance to the media list.
func (ml *MediaList) AddMedia(m *Media) error {
	if m == nil || m.media == nil {
		return ErrMediaNotInitialized
	}
	if err := ml.Lock(); err != nil {
		return err
	}
	defer ml.Unlock()

	C.libvlc_media_list_add_media(ml.list, m.media)
	return getError()
}

// AddMediaFromPath loads a media file from path and adds it
// to the the media list.
func (ml *MediaList) AddMediaFromPath(path string) error {
	media, err := NewMediaFromPath(path)
	if err != nil {
		return err
	}

	return ml.AddMedia(media)
}

// AddMediaFromURL loads a media file from url and adds it
// to the the media list.
func (ml *MediaList) AddMediaFromURL(url string) error {
	media, err := NewMediaFromURL(url)
	if err != nil {
		return err
	}

	return ml.AddMedia(media)
}

// Lock makes the caller the current owner of the media list.
func (ml *MediaList) Lock() error {
	if ml.list == nil {
		return ErrMediaListNotInitialized
	}

	C.libvlc_media_list_lock(ml.list)
	return getError()
}

// Unlock releases ownership of the media list.
func (ml *MediaList) Unlock() error {
	if ml.list == nil {
		return ErrMediaListNotInitialized
	}

	C.libvlc_media_list_unlock(ml.list)
	return getError()
}

// EventManager returns the event manager responsible for the media list.
func (ml *MediaList) EventManager() (*EventManager, error) {
	if ml.list == nil {
		return nil, ErrMediaListNotInitialized
	}

	manager := C.libvlc_media_list_event_manager(ml.list)
	if manager == nil {
		return nil, ErrMissingEventManager
	}

	return newEventManager(manager), nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/davecgh/go-spew/spew"
	"github.com/gophergala2016/Pulse/pulse"
	"github.com/gophergala2016/Pulse/pulse/config"
)

var (
	def  bool
	api  bool
	smtp string
)

func init() {
	flag.BoolVar(&def, "d", false, "Turn on default mode")
	flag.StringVar(&smtp, "smtp", "SMTP.toml", "Config location of SMTP.toml")
	flag.Parse()
}

func main() {
	if len(os.Args[1:]) == 0 {
		startAPI()
	} else {
		startPulse()
	}
}

func startAPI() {
	spew.Println("API Mode")
}

func startPulse() {
	var stdIn = make(chan string)
	if def {
		smtpCfg, err := config.LoadSMTP("C:\\Users\\dixon\\Go\\src\\github.com\\gophergala2016\\Pulse\\pulse\\cmd\\pulse\\SMTP.toml")
		if err != nil {
			panic(err)
		}
		spew.Dump(smtpCfg)
		secretCfg, err := config.LoadSecret("C:\\Users\\dixon\\Go\\src\\github.com\\gophergala2016\\Pulse\\pulse\\cmd\\pulse\\secret.toml")
		if err != nil {
			panic(err)
		}
		spew.Dump(secretCfg)
		cfg, err := config.Load("C:\\Users\\dixon\\Go\\src\\github.com\\gophergala2016\\Pulse\\pulse\\cmd\\pulse\\PulseConfig.toml")
		if err != nil {
			panic(err)
		}
		spew.Dump(cfg)
		pulse.Run(stdIn, printFunc)
		stdIn <- "Hello World"
		stdIn <- "Because Tesla"
		stdIn <- "Why not"

	} else {
		spew.Println("Reading files from command line")
		for _, arg := range flag.Args() {
			spew.Dump(arg)
		}
	}
}

func printFunc(value string) {
	fmt.Println(value)
}
<commit_msg>Reconfigured main.go<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/davecgh/go-spew/spew"
	"github.com/gophergala2016/Pulse/pulse/config"
)

var (
	def      bool
	smtpPath string
	cfgPath  string
)

func init() {
	flag.BoolVar(&def, "d", false, "Turn on default mode")
	flag.StringVar(&smtpPath, "smtp", "SMTP.toml", "Config location of SMTP.toml")
	flag.StringVar(&cfgPath, "cfg", "PulseConfig.toml", "Config locaton of PulseConfig.toml")
	flag.Parse()

	if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
		panic(fmt.Errorf("Could not find %s", cfgPath))
	}
}

func main() {
	if len(flag.Args()) == 0 && !def {
		startAPI()
	} else if def {
		cfg, err := config.Load(cfgPath)
		if err != nil {
			panic(fmt.Errorf("Could not load the config.\n %v", err))
		}
		if len(cfg.LogList) == 0 {
			panic(fmt.Errorf("Must supply a list of log files in the config."))
		}
		startPulse(cfg.LogList)
	} else {
		startPulse(flag.Args())
	}
}

func startAPI() {
	spew.Println("API Mode")
}

func startPulse(filenames []string) {
	errChan := make(chan error)
	go func(errChan chan error) {
		for _, filename := range filenames {
			if _, err := os.Stat(filename); os.IsNotExist(err) {
				errChan <- fmt.Errorf("Could not find %s", filename)
			}
		}
		close(errChan)
	}(errChan)

	for err := range errChan {
		panic(err)
	}
}

func printFunc(value string) {
	fmt.Println(value)
}
<|endoftext|>


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

package framework

import (
	"net/http"
	"strings"
	"testing"
	"time"

	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	monitoringv1alpha1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
	"github.com/coreos/prometheus-operator/pkg/k8sutil"
	"github.com/pkg/errors"
)

type Framework struct {
	KubeClient        kubernetes.Interface
	MonClientV1       monitoringv1.MonitoringV1Interface
	MonClientV1alpha1 monitoringv1alpha1.MonitoringV1alpha1Interface
	HTTPClient        *http.Client
	MasterHost        string
	Namespace         *v1.Namespace
	OperatorPod       *v1.Pod
	DefaultTimeout    time.Duration
}

// Setup setups a test framework and returns it.
func New(ns, kubeconfig, opImage string) (*Framework, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, errors.Wrap(err, "build config from flags failed")
	}

	cli, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "creating new kube-client failed")
	}

	httpc := cli.CoreV1().RESTClient().(*rest.RESTClient).Client
	if err != nil {
		return nil, errors.Wrap(err, "creating http-client failed")
	}

	mClientV1, err := monitoringv1.NewForConfig(&monitoringv1.DefaultCrdKinds, monitoringv1.Group, config)
	if err != nil {
		return nil, errors.Wrap(err, "creating v1 monitoring client failed")
	}

	mClientV1alpha1, err := monitoringv1alpha1.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "creating v1alpha1 monitoring client failed")
	}

	namespace, err := CreateNamespace(cli, ns)
	if err != nil {
		return nil, err
	}

	f := &Framework{
		MasterHost:        config.Host,
		KubeClient:        cli,
		MonClientV1:       mClientV1,
		MonClientV1alpha1: mClientV1alpha1,
		HTTPClient:        httpc,
		Namespace:         namespace,
		DefaultTimeout:    time.Minute,
	}

	err = f.Setup(opImage)
	if err != nil {
		return nil, errors.Wrap(err, "setup test environment failed")
	}

	return f, nil
}

func (f *Framework) Setup(opImage string) error {
	if err := f.setupPrometheusOperator(opImage); err != nil {
		return errors.Wrap(err, "setup prometheus operator failed")
	}

	return nil
}

func (f *Framework) setupPrometheusOperator(opImage string) error {
	if _, err := CreateServiceAccount(f.KubeClient, f.Namespace.Name, "../../example/rbac/prometheus-operator/prometheus-operator-service-account.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus operator service account")
	}

	if err := CreateClusterRole(f.KubeClient, "../../example/rbac/prometheus-operator/prometheus-operator-cluster-role.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus cluster role")
	}

	if _, err := CreateClusterRoleBinding(f.KubeClient, f.Namespace.Name, "../../example/rbac/prometheus-operator/prometheus-operator-cluster-role-binding.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus cluster role binding")
	}

	if err := CreateClusterRole(f.KubeClient, "../../example/rbac/prometheus/prometheus-cluster-role.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus cluster role")
	}

	deploy, err := MakeDeployment("../../example/rbac/prometheus-operator/prometheus-operator-deployment.yaml")
	if err != nil {
		return err
	}

	if opImage != "" {
		// Override operator image used, if specified when running tests.
		deploy.Spec.Template.Spec.Containers[0].Image = opImage
		repoAndTag := strings.Split(opImage, ":")
		if len(repoAndTag) != 2 {
			return errors.Errorf(
				"expected operator image '%v' split by colon to result in two substrings but got '%v'",
				opImage,
				repoAndTag,
			)
		}
		deploy.Spec.Template.Spec.Containers[0].Args[1] = "--prometheus-config-reloader=" +
			"quay.io/coreos/prometheus-config-reloader:" +
			repoAndTag[1]
	}

	deploy.Spec.Template.Spec.Containers[0].Args = append(deploy.Spec.Template.Spec.Containers[0].Args, "--log-level=all")

	err = CreateDeployment(f.KubeClient, f.Namespace.Name, deploy)
	if err != nil {
		return err
	}

	opts := metav1.ListOptions{LabelSelector: fields.SelectorFromSet(fields.Set(deploy.Spec.Template.ObjectMeta.Labels)).String()}
	err = WaitForPodsReady(f.KubeClient, f.Namespace.Name, f.DefaultTimeout, 1, opts)
	if err != nil {
		return errors.Wrap(err, "failed to wait for prometheus operator to become ready")
	}

	pl, err := f.KubeClient.Core().Pods(f.Namespace.Name).List(opts)
	if err != nil {
		return err
	}
	f.OperatorPod = &pl.Items[0]

	return nil
}

func (ctx *TestCtx) SetupPrometheusRBAC(t *testing.T, ns string, kubeClient kubernetes.Interface) {
	if finalizerFn, err := CreateServiceAccount(kubeClient, ns, "../../example/rbac/prometheus/prometheus-service-account.yaml"); err != nil {
		t.Fatal(errors.Wrap(err, "failed to create prometheus service account"))
	} else {
		ctx.AddFinalizerFn(finalizerFn)
	}

	if finalizerFn, err := CreateRoleBinding(kubeClient, ns, "../framework/ressources/prometheus-role-binding.yml"); err != nil {
		t.Fatal(errors.Wrap(err, "failed to create prometheus role binding"))
	} else {
		ctx.AddFinalizerFn(finalizerFn)
	}
}

// Teardown tears down a previously initialized test environment.
func (f *Framework) Teardown() error {
	if err := f.KubeClient.Core().Services(f.Namespace.Name).Delete("prometheus-operated", nil); err != nil && !k8sutil.IsResourceNotFoundError(err) {
		return err
	}

	if err := f.KubeClient.Core().Services(f.Namespace.Name).Delete("alertmanager-operated", nil); err != nil && !k8sutil.IsResourceNotFoundError(err) {
		return err
	}

	if err := f.KubeClient.Extensions().Deployments(f.Namespace.Name).Delete("prometheus-operator", nil); err != nil {
		return err
	}
	if err := DeleteNamespace(f.KubeClient, f.Namespace.Name); err != nil {
		return err
	}

	return nil
}
<commit_msg>test/e2e: Fix wrong override of Prometheus config reloader arg<commit_after>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package framework

import (
	"net/http"
	"strings"
	"testing"
	"time"

	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	monitoringv1alpha1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
	"github.com/coreos/prometheus-operator/pkg/k8sutil"
	"github.com/pkg/errors"
)

type Framework struct {
	KubeClient        kubernetes.Interface
	MonClientV1       monitoringv1.MonitoringV1Interface
	MonClientV1alpha1 monitoringv1alpha1.MonitoringV1alpha1Interface
	HTTPClient        *http.Client
	MasterHost        string
	Namespace         *v1.Namespace
	OperatorPod       *v1.Pod
	DefaultTimeout    time.Duration
}

// Setup setups a test framework and returns it.
func New(ns, kubeconfig, opImage string) (*Framework, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, errors.Wrap(err, "build config from flags failed")
	}

	cli, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "creating new kube-client failed")
	}

	httpc := cli.CoreV1().RESTClient().(*rest.RESTClient).Client
	if err != nil {
		return nil, errors.Wrap(err, "creating http-client failed")
	}

	mClientV1, err := monitoringv1.NewForConfig(&monitoringv1.DefaultCrdKinds, monitoringv1.Group, config)
	if err != nil {
		return nil, errors.Wrap(err, "creating v1 monitoring client failed")
	}

	mClientV1alpha1, err := monitoringv1alpha1.NewForConfig(config)
	if err != nil {
		return nil, errors.Wrap(err, "creating v1alpha1 monitoring client failed")
	}

	namespace, err := CreateNamespace(cli, ns)
	if err != nil {
		return nil, err
	}

	f := &Framework{
		MasterHost:        config.Host,
		KubeClient:        cli,
		MonClientV1:       mClientV1,
		MonClientV1alpha1: mClientV1alpha1,
		HTTPClient:        httpc,
		Namespace:         namespace,
		DefaultTimeout:    time.Minute,
	}

	err = f.Setup(opImage)
	if err != nil {
		return nil, errors.Wrap(err, "setup test environment failed")
	}

	return f, nil
}

func (f *Framework) Setup(opImage string) error {
	if err := f.setupPrometheusOperator(opImage); err != nil {
		return errors.Wrap(err, "setup prometheus operator failed")
	}

	return nil
}

func (f *Framework) setupPrometheusOperator(opImage string) error {
	if _, err := CreateServiceAccount(f.KubeClient, f.Namespace.Name, "../../example/rbac/prometheus-operator/prometheus-operator-service-account.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus operator service account")
	}

	if err := CreateClusterRole(f.KubeClient, "../../example/rbac/prometheus-operator/prometheus-operator-cluster-role.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus cluster role")
	}

	if _, err := CreateClusterRoleBinding(f.KubeClient, f.Namespace.Name, "../../example/rbac/prometheus-operator/prometheus-operator-cluster-role-binding.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus cluster role binding")
	}

	if err := CreateClusterRole(f.KubeClient, "../../example/rbac/prometheus/prometheus-cluster-role.yaml"); err != nil && !apierrors.IsAlreadyExists(err) {
		return errors.Wrap(err, "failed to create prometheus cluster role")
	}

	deploy, err := MakeDeployment("../../example/rbac/prometheus-operator/prometheus-operator-deployment.yaml")
	if err != nil {
		return err
	}

	if opImage != "" {
		// Override operator image used, if specified when running tests.
		deploy.Spec.Template.Spec.Containers[0].Image = opImage
		repoAndTag := strings.Split(opImage, ":")
		if len(repoAndTag) != 2 {
			return errors.Errorf(
				"expected operator image '%v' split by colon to result in two substrings but got '%v'",
				opImage,
				repoAndTag,
			)
		}
		// Override Prometheus config reloader image
		for i, arg := range deploy.Spec.Template.Spec.Containers[0].Args {
			if strings.Contains(arg, "--prometheus-config-reloader=") {
				deploy.Spec.Template.Spec.Containers[0].Args[i] = "--prometheus-config-reloader=" +
					"quay.io/coreos/prometheus-config-reloader:" +
					repoAndTag[1]
			}
		}
	}

	deploy.Spec.Template.Spec.Containers[0].Args = append(deploy.Spec.Template.Spec.Containers[0].Args, "--log-level=all")

	err = CreateDeployment(f.KubeClient, f.Namespace.Name, deploy)
	if err != nil {
		return err
	}

	opts := metav1.ListOptions{LabelSelector: fields.SelectorFromSet(fields.Set(deploy.Spec.Template.ObjectMeta.Labels)).String()}
	err = WaitForPodsReady(f.KubeClient, f.Namespace.Name, f.DefaultTimeout, 1, opts)
	if err != nil {
		return errors.Wrap(err, "failed to wait for prometheus operator to become ready")
	}

	pl, err := f.KubeClient.Core().Pods(f.Namespace.Name).List(opts)
	if err != nil {
		return err
	}
	f.OperatorPod = &pl.Items[0]

	return nil
}

func (ctx *TestCtx) SetupPrometheusRBAC(t *testing.T, ns string, kubeClient kubernetes.Interface) {
	if finalizerFn, err := CreateServiceAccount(kubeClient, ns, "../../example/rbac/prometheus/prometheus-service-account.yaml"); err != nil {
		t.Fatal(errors.Wrap(err, "failed to create prometheus service account"))
	} else {
		ctx.AddFinalizerFn(finalizerFn)
	}

	if finalizerFn, err := CreateRoleBinding(kubeClient, ns, "../framework/ressources/prometheus-role-binding.yml"); err != nil {
		t.Fatal(errors.Wrap(err, "failed to create prometheus role binding"))
	} else {
		ctx.AddFinalizerFn(finalizerFn)
	}
}

// Teardown tears down a previously initialized test environment.
func (f *Framework) Teardown() error {
	if err := f.KubeClient.Core().Services(f.Namespace.Name).Delete("prometheus-operated", nil); err != nil && !k8sutil.IsResourceNotFoundError(err) {
		return err
	}

	if err := f.KubeClient.Core().Services(f.Namespace.Name).Delete("alertmanager-operated", nil); err != nil && !k8sutil.IsResourceNotFoundError(err) {
		return err
	}

	if err := f.KubeClient.Extensions().Deployments(f.Namespace.Name).Delete("prometheus-operator", nil); err != nil {
		return err
	}
	if err := DeleteNamespace(f.KubeClient, f.Namespace.Name); err != nil {
		return err
	}

	return nil
}
<|endoftext|>