<commit_before>package clockwork

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
)

// DeliveryState status of a message
type DeliveryState byte

const (
	// Queued for delivery to mobile networks
	Queued DeliveryState = iota
	// Enroute sent to mobile network
	Enroute
	// Delivered to destination
	Delivered
	// Expired Message validity period has expired. Handset turned off or out of
	// range
	Expired
	// Deleted Message has been deleted
	Deleted
	// Undelivered Message could not be delivered
	Undelivered
	// Accepted Message is in accepted state. Message has been read manually on
	// behalf of the subscriber by customer service
	Accepted
	// Unknown No final delivery status received from the network
	Unknown
	// Rejected Message rejected by the mobile network
	Rejected
)

// String returns delivery state as a string
func (d DeliveryState) String() string {
	switch d {
	case Queued:
		return "Queued"
	case Enroute:
		return "Enroute"
	case Delivered:
		return "Delivered"
	case Expired:
		return "Expired"
	case Deleted:
		return "Deleted"
	case Undelivered:
		return "Undelivered"
	case Accepted:
		return "Accepted"
	case Unknown:
		return "Unknown"
	case Rejected:
		return "Rejected"
	default:
		return "!ERROR!" // debug
	}
}

// Receipt message delivery receipt
type Receipt struct {
	ID     string
	To     string
	Status DeliveryState
	Err    error
}

// ReceiptCallback callback for new delivery receipt
type ReceiptCallback func(Receipt)

// ReceiptHandler custom handler which processes delivery receipts
type ReceiptHandler struct {
	// Path prefixed with "/" i.e. "/delivery-receipts", "/sms"
	Path string
	// Port to listen on
	Port int
	// Callback called when a new receipt has been received
	Callback ReceiptCallback
}

// ServeHTTP process delivery receipts
func (rc *ReceiptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vals := url.Values{}
	if r.Method == "GET" {
		vals = r.URL.Query()
	}

	if r.Method == "POST" {
		body, err := ioutil.ReadAll(r.Body)

		if err != nil {
			log.Println(err)
			return
		}

		vals, err = url.ParseQuery(string(body))
		if err != nil {
			log.Println(err)
			return
		}
	}

	if len(vals) == 0 {
		log.Println("serve http: query values map is zero")
		return
	}

	var receipt Receipt
	if v, ok := vals["msg_id"]; ok {
		receipt.ID = v[0]
	}

	if v, ok := vals["to"]; ok {
		receipt.To = v[0]
	}

	if v, ok := vals["status"]; ok {
		receipt.Status = toDeliveryState(v[0])
	}

	if v, ok := vals["detail"]; ok {
		receipt.Err = errorFromDetailCode(v[0])
	}

	// invoke callback
	rc.Callback(receipt)

	// need to respond with a 200 OK status code to acknowledge receipt of the
	// message, otherwise the clockwork API will retry at regular intervals.
	w.WriteHeader(http.StatusOK)
}

// DeliveryReceiptListen listen for incoming delivery receipts.
func DeliveryReceiptListen(rh *ReceiptHandler) error {
	if rh.Callback == nil {
		return errors.New("callback is nil")
	}
	if rh.Path == "" {
		return errors.New("path is empty")
	}
	mux := http.NewServeMux()
	mux.Handle(rh.Path, rh)
	p := fmt.Sprintf(":%d", rh.Port)
	return http.ListenAndServe(p, mux)
}

// toDeliveryState converts clockwork delivery status strings to the
// DeliveryState type. See:
// https://www.clockworksms.com/doc/reference/faqs/delivery-states/
func toDeliveryState(s string) DeliveryState {
	switch s {
	case "QUEUED":
		return Queued
	case "ENROUTE":
		return Enroute
	case "DELIVRD":
		return Delivered
	case "EXPIRED":
		return Expired
	case "DELETED":
		return Deleted
	case "UNDELIV":
		return Undelivered
	case "ACCEPTD":
		return Accepted
	case "UNKNOWN":
		return Unknown
	case "REJECTD":
		return Rejected
	default:
		return 126 // debug
	}
}

// errorFromDetailCode provides more information on why a message has failed.
// Sometimes the mobile network doesn’t provide a reason, in these cases it will
// be set to nil. See:
// https://www.clockworksms.com/doc/reference/faqs/delivery-states/
func errorFromDetailCode(code string) error {
	switch code {
	case "2":
		return ErrMessageDetailsWrong
	case "3":
		return ErrPermOperator
	case "4":
		return ErrTempOperator
	case "5":
		return ErrPermAbsentSub
	case "6":
		return ErrTempAbsentSub
	case "9":
		return ErrPermPhone
	case "10":
		return ErrTempPhone
	default:
		return nil
	}
}
<commit_msg>Add Time Field to Delivery Receipts<commit_after>package clockwork

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

// DeliveryState status of a message
type DeliveryState byte

const (
	// Queued for delivery to mobile networks
	Queued DeliveryState = iota
	// Enroute sent to mobile network
	Enroute
	// Delivered to destination
	Delivered
	// Expired Message validity period has expired. Handset turned off or out of
	// range
	Expired
	// Deleted Message has been deleted
	Deleted
	// Undelivered Message could not be delivered
	Undelivered
	// Accepted Message is in accepted state. Message has been read manually on
	// behalf of the subscriber by customer service
	Accepted
	// Unknown No final delivery status received from the network
	Unknown
	// Rejected Message rejected by the mobile network
	Rejected
)

// String returns delivery state as a string
func (d DeliveryState) String() string {
	switch d {
	case Queued:
		return "Queued"
	case Enroute:
		return "Enroute"
	case Delivered:
		return "Delivered"
	case Expired:
		return "Expired"
	case Deleted:
		return "Deleted"
	case Undelivered:
		return "Undelivered"
	case Accepted:
		return "Accepted"
	case Unknown:
		return "Unknown"
	case Rejected:
		return "Rejected"
	default:
		return "!ERROR!" // debug
	}
}

// Receipt message delivery receipt
type Receipt struct {
	ID     string
	To     string
	Status DeliveryState
	Time   time.Time
	Err    error
}

// ReceiptCallback callback for new delivery receipt
type ReceiptCallback func(Receipt)

// ReceiptHandler custom handler which processes delivery receipts
type ReceiptHandler struct {
	// Path prefixed with "/" i.e. "/delivery-receipts", "/sms"
	Path string
	// Port to listen on
	Port int
	// Callback called when a new receipt has been received
	Callback ReceiptCallback
}

// ServeHTTP process delivery receipts
func (rc *ReceiptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vals := url.Values{}
	if r.Method == "GET" {
		vals = r.URL.Query()
	}

	if r.Method == "POST" {
		body, err := ioutil.ReadAll(r.Body)

		if err != nil {
			log.Println(err)
			return
		}

		vals, err = url.ParseQuery(string(body))
		if err != nil {
			log.Println(err)
			return
		}
	}

	if len(vals) == 0 {
		log.Println("serve http: query values map is zero")
		return
	}

	var receipt Receipt
	if v, ok := vals["msg_id"]; ok {
		receipt.ID = v[0]
	}

	if v, ok := vals["to"]; ok {
		receipt.To = v[0]
	}

	if v, ok := vals["status"]; ok {
		receipt.Status = toDeliveryState(v[0])
	}

	if v, ok := vals["detail"]; ok {
		receipt.Err = errorFromDetailCode(v[0])
	}

	receipt.Time = time.Now().UTC()

	// invoke callback
	rc.Callback(receipt)

	// need to respond with a 200 OK status code to acknowledge receipt of the
	// message, otherwise the clockwork API will retry at regular intervals.
	w.WriteHeader(http.StatusOK)
}

// DeliveryReceiptListen listen for incoming delivery receipts.
func DeliveryReceiptListen(rh *ReceiptHandler) error {
	if rh.Callback == nil {
		return errors.New("callback is nil")
	}
	if rh.Path == "" {
		return errors.New("path is empty")
	}
	mux := http.NewServeMux()
	mux.Handle(rh.Path, rh)
	p := fmt.Sprintf(":%d", rh.Port)
	return http.ListenAndServe(p, mux)
}

// toDeliveryState converts clockwork delivery status strings to the
// DeliveryState type. See:
// https://www.clockworksms.com/doc/reference/faqs/delivery-states/
func toDeliveryState(s string) DeliveryState {
	switch s {
	case "QUEUED":
		return Queued
	case "ENROUTE":
		return Enroute
	case "DELIVRD":
		return Delivered
	case "EXPIRED":
		return Expired
	case "DELETED":
		return Deleted
	case "UNDELIV":
		return Undelivered
	case "ACCEPTD":
		return Accepted
	case "UNKNOWN":
		return Unknown
	case "REJECTD":
		return Rejected
	default:
		return 126 // debug
	}
}

// errorFromDetailCode provides more information on why a message has failed.
// Sometimes the mobile network doesn’t provide a reason, in these cases it will
// be set to nil. See:
// https://www.clockworksms.com/doc/reference/faqs/delivery-states/
func errorFromDetailCode(code string) error {
	switch code {
	case "2":
		return ErrMessageDetailsWrong
	case "3":
		return ErrPermOperator
	case "4":
		return ErrTempOperator
	case "5":
		return ErrPermAbsentSub
	case "6":
		return ErrTempAbsentSub
	case "9":
		return ErrPermPhone
	case "10":
		return ErrTempPhone
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>// GENERATED CODE - DO NOT EDIT
package routes

import "github.com/revel/revel"

type tApp struct {}
var App tApp

func (_ tApp) Search(
		query string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "query", query)
	return revel.MainRouter.Reverse("App.Search", args).Url
}

func (_ tApp) Details(
		query string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "query", query)
	return revel.MainRouter.Reverse("App.Details", args).Url
}

func (_ tApp) SearchGet(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.SearchGet", args).Url
}

func (_ tApp) About(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.About", args).Url
}

func (_ tApp) Resources(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Resources", args).Url
}

func (_ tApp) SavePhrase(
		phrase string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "phrase", phrase)
	return revel.MainRouter.Reverse("App.SavePhrase", args).Url
}

func (_ tApp) Register(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Register", args).Url
}

func (_ tApp) LoginPage(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.LoginPage", args).Url
}

func (_ tApp) Profile(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Profile", args).Url
}

func (_ tApp) SaveUser(
		user interface{},
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "user", user)
	return revel.MainRouter.Reverse("App.SaveUser", args).Url
}

func (_ tApp) Login(
		email string,
		password string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "email", email)
	revel.Unbind(args, "password", password)
	return revel.MainRouter.Reverse("App.Login", args).Url
}

func (_ tApp) Logout(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Logout", args).Url
}

func (_ tApp) Index(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Index", args).Url
}

type tStatic struct {}
var Static tStatic

func (_ tStatic) Serve(
		prefix string,
		filepath string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "prefix", prefix)
	revel.Unbind(args, "filepath", filepath)
	return revel.MainRouter.Reverse("Static.Serve", args).Url
}

func (_ tStatic) ServeModule(
		moduleName string,
		prefix string,
		filepath string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "moduleName", moduleName)
	revel.Unbind(args, "prefix", prefix)
	revel.Unbind(args, "filepath", filepath)
	return revel.MainRouter.Reverse("Static.ServeModule", args).Url
}

type tTestRunner struct {}
var TestRunner tTestRunner

func (_ tTestRunner) Index(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("TestRunner.Index", args).Url
}

func (_ tTestRunner) Run(
		suite string,
		test string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "suite", suite)
	revel.Unbind(args, "test", test)
	return revel.MainRouter.Reverse("TestRunner.Run", args).Url
}

func (_ tTestRunner) List(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("TestRunner.List", args).Url
}

<commit_msg>Update routes<commit_after>// GENERATED CODE - DO NOT EDIT
package routes

import "github.com/revel/revel"

type tApp struct {}
var App tApp

func (_ tApp) Search(
		query string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "query", query)
	return revel.MainRouter.Reverse("App.Search", args).Url
}

func (_ tApp) Details(
		query string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "query", query)
	return revel.MainRouter.Reverse("App.Details", args).Url
}

func (_ tApp) SearchGet(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.SearchGet", args).Url
}

func (_ tApp) About(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.About", args).Url
}

func (_ tApp) Resources(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Resources", args).Url
}

func (_ tApp) SavePhrase(
		phrase string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "phrase", phrase)
	return revel.MainRouter.Reverse("App.SavePhrase", args).Url
}

func (_ tApp) Register(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Register", args).Url
}

func (_ tApp) LoginPage(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.LoginPage", args).Url
}

func (_ tApp) Profile(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Profile", args).Url
}

func (_ tApp) SaveUser(
		user interface{},
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "user", user)
	return revel.MainRouter.Reverse("App.SaveUser", args).Url
}

func (_ tApp) Login(
		email string,
		password string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "email", email)
	revel.Unbind(args, "password", password)
	return revel.MainRouter.Reverse("App.Login", args).Url
}

func (_ tApp) Logout(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Logout", args).Url
}

func (_ tApp) Index(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("App.Index", args).Url
}

type tTestRunner struct {}
var TestRunner tTestRunner

func (_ tTestRunner) Index(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("TestRunner.Index", args).Url
}

func (_ tTestRunner) Run(
		suite string,
		test string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "suite", suite)
	revel.Unbind(args, "test", test)
	return revel.MainRouter.Reverse("TestRunner.Run", args).Url
}

func (_ tTestRunner) List(
		) string {
	args := make(map[string]string)

	return revel.MainRouter.Reverse("TestRunner.List", args).Url
}

type tStatic struct {}
var Static tStatic

func (_ tStatic) Serve(
		prefix string,
		filepath string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "prefix", prefix)
	revel.Unbind(args, "filepath", filepath)
	return revel.MainRouter.Reverse("Static.Serve", args).Url
}

func (_ tStatic) ServeModule(
		moduleName string,
		prefix string,
		filepath string,
		) string {
	args := make(map[string]string)

	revel.Unbind(args, "moduleName", moduleName)
	revel.Unbind(args, "prefix", prefix)
	revel.Unbind(args, "filepath", filepath)
	return revel.MainRouter.Reverse("Static.ServeModule", args).Url
}

<|endoftext|>


<commit_before>package goapns

import (
	"bytes"
	"crypto/tls"
	"net/http"

	"encoding/json"
	"fmt"

	"golang.org/x/net/http2"
)

type Connection struct {
	HTTPClient  http.Client
	Certificate tls.Certificate
	Host        string
}

// Apple HTTP/2 Development & Production urls
const (
	HostDevelopment = "https://api.development.push.apple.com"
	HostProduction  = "https://api.push.apple.com"
)

func NewConnection(pathname string, key string) (*Connection, error) {
	c := &Connection{}

	cert, err := CertificateFromP12(pathname, key)
	if err != nil {
		//fmt.Printf("Error creating Connection: %v", err)
		return nil, err
	}
	c.Certificate = cert

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	//no check if Certificate is present to fail hard if this requirement is not met
	tlsConfig.BuildNameToCertificate()

	transport := &http2.Transport{TLSClientConfig: tlsConfig}

	c.HTTPClient = http.Client{Transport: transport}
	//Default Host is Development Host.
	c.Host = HostDevelopment
	return c, nil
}

func (c *Connection) Development() *Connection {
	c.Host = HostDevelopment
	return c
}
func (c *Connection) Production() *Connection {
	c.Host = HostProduction
	return c
}

func (c *Connection) Push(message *Message, tokens []string, responseChannel chan Response) {
	fmt.Printf("Will push to tokens %v \n", tokens)
	dataToSend, err := json.Marshal(message)

	if err != nil {
		fmt.Printf("Error JSONING the response: %v\naborting\n", err)
		close(responseChannel)
		return
	}

	for index, token := range tokens {

		url := fmt.Sprintf("%v/3/device/%v", c.Host, token)
		request, err := http.NewRequest("POST", url, bytes.NewBuffer(dataToSend))
		if err != nil {
			fmt.Printf("Error creating request: %v\naborting\n", err)
			response := Response{}
			response.Error = err
			response.Message = message
			responseChannel <- response
			continue
		}

		configureHeader(request, message)
		push := func(token string, responseChannel chan Response) {

			httpResponse, err := c.HTTPClient.Do(request)
			if err != nil {
				fmt.Printf("Error during response: %v\naborting\n", err)

				response := Response{}
				response.Error = err
				response.Message = message
				responseChannel <- response
			} else {
				defer httpResponse.Body.Close()

				//Response object that will be populated and passed into the responseChannel
				var response Response

				if httpResponse.StatusCode != http.StatusOK {
					//Something went wrong, creating new Response object from the JSON response
					errParsingJSON := json.NewDecoder(httpResponse.Body).Decode(&response)
					if errParsingJSON != nil {
						//We could not parse the response into JSON, we need to pass the received error into the responseChannel
						response.Error = err

					} else {
						//We have parsed the error and populated a new Response object with it.

						//Converting the JSON body (string) into an error object
						knownError, found := errorReason[response.Reason]

						if !found {
							//We could not find the error in our map so we try to use the HTTP status code to produce some meaningful error object
							knownError, found = errorStatus[httpResponse.StatusCode]

							if !found {
								//Could not find the error anywhere :(
								knownError = ErrorUnknown
							}
						}
						response.Error = knownError
					}
				}

				response.Message = message
				response.Token = token
				response.StatusCode = httpResponse.StatusCode
				responseChannel <- response
			}
		}
		go push(token, responseChannel)
		if index == len(tokens) {
			close(responseChannel)
		}
	}

}

func configureHeader(request *http.Request, message *Message) {
	request.Header.Set("Content-Type", "application/json; charset=utf-8")

	if message.Header.APNSID != "" {
		request.Header.Set("apns-id", message.Header.APNSID)
	}

	if !message.Header.Expiration.IsZero() {
		request.Header.Set("apns-expiration", fmt.Sprintf("%v", message.Header.Expiration.Unix()))
	}
	//Only set the priority if it is low because high is the default
	//value that is assumed when no priority is specified.
	//We want to omit everything we can to save bandwith.
	if message.Header.Priority == PriorityLow {
		request.Header.Set("apns-priority", fmt.Sprintf("%v", message.Header.Priority))
	}
	if message.Header.Topic != "" {
		request.Header.Set("apns-topic", message.Header.Topic)
	}
}
<commit_msg>Closing responseChannel when done correctly<commit_after>package goapns

import (
	"bytes"
	"crypto/tls"
	"net/http"

	"encoding/json"
	"fmt"

	"golang.org/x/net/http2"
)

type Connection struct {
	HTTPClient  http.Client
	Certificate tls.Certificate
	Host        string
}

// Apple HTTP/2 Development & Production urls
const (
	HostDevelopment = "https://api.development.push.apple.com"
	HostProduction  = "https://api.push.apple.com"
)

func NewConnection(pathname string, key string) (*Connection, error) {
	c := &Connection{}

	cert, err := CertificateFromP12(pathname, key)
	if err != nil {
		//fmt.Printf("Error creating Connection: %v", err)
		return nil, err
	}
	c.Certificate = cert

	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	//no check if Certificate is present to fail hard if this requirement is not met
	tlsConfig.BuildNameToCertificate()

	transport := &http2.Transport{TLSClientConfig: tlsConfig}

	c.HTTPClient = http.Client{Transport: transport}
	//Default Host is Development Host.
	c.Host = HostDevelopment
	return c, nil
}

func (c *Connection) Development() *Connection {
	c.Host = HostDevelopment
	return c
}
func (c *Connection) Production() *Connection {
	c.Host = HostProduction
	return c
}

func (c *Connection) Push(message *Message, tokens []string, responseChannel chan Response) {
	fmt.Printf("Will push to tokens %v , URL: %v\n", tokens, c.Host)
	dataToSend, err := json.Marshal(message)

	if err != nil {
		fmt.Printf("Error JSONING the request: %v\naborting\n", err)
		close(responseChannel)
		return
	}

	for index, token := range tokens {

		url := fmt.Sprintf("%v/3/device/%v", c.Host, token)
		request, err := http.NewRequest("POST", url, bytes.NewBuffer(dataToSend))
		if err != nil {
			fmt.Printf("Error creating request: %v\naborting\n", err)
			response := Response{}
			response.Error = err
			response.Message = message
			responseChannel <- response
			continue
		}

		configureHeader(request, message)
		push := func(token string, responseChannel chan Response, shouldCloseChannelWhenDone bool) {
			if shouldCloseChannelWhenDone {
				defer close(responseChannel)

				fmt.Printf("Closing channel\n")
			} else {
				fmt.Printf("was pushing to token %v with index %v\n", token, index)
			}

			httpResponse, err := c.HTTPClient.Do(request)
			if httpResponse != nil {
				defer httpResponse.Body.Close()
			}

			if err != nil {
				fmt.Printf("Error during response: %v\nAborting.\n", err)

				response := Response{}
				response.Error = err
				response.Message = message
				response.Token = token
				responseChannel <- response
			} else {

				//Response object that will be populated and passed into the responseChannel
				var response Response

				if httpResponse.StatusCode != http.StatusOK {
					//Something went wrong, creating new Response object from the JSON response
					errParsingJSON := json.NewDecoder(httpResponse.Body).Decode(&response)
					if errParsingJSON != nil {
						//We could not parse the response into JSON, we need to pass the received error into the responseChannel
						response.Error = err

					} else {
						//We have parsed the error and populated a new Response object with it.

						//Converting the JSON body (string) into an error object
						knownError, found := errorReason[response.Reason]

						if !found {
							//We could not find the error in our map so we try to use the HTTP status code to produce some meaningful error object
							knownError, found = errorStatus[httpResponse.StatusCode]

							if !found {
								//Could not find the error anywhere :(
								knownError = ErrorUnknown
							}
						}
						response.Error = knownError
					}
				}

				response.Message = message
				response.Token = token
				response.StatusCode = httpResponse.StatusCode
				responseChannel <- response
			}
		}
		shouldCloseChannelWhenDone := index == len(tokens)-1
		go push(token, responseChannel, shouldCloseChannelWhenDone)

	}

}

func configureHeader(request *http.Request, message *Message) {
	request.Header.Set("Content-Type", "application/json; charset=utf-8")

	if message.Header.APNSID != "" {
		request.Header.Set("apns-id", message.Header.APNSID)
	}

	if !message.Header.Expiration.IsZero() {
		request.Header.Set("apns-expiration", fmt.Sprintf("%v", message.Header.Expiration.Unix()))
	}
	//Only set the priority if it is low because high is the default
	//value that is assumed when no priority is specified.
	//We want to omit everything we can to save bandwith.
	if message.Header.Priority == PriorityLow {
		request.Header.Set("apns-priority", fmt.Sprintf("%v", message.Header.Priority))
	}
	if message.Header.Topic != "" {
		request.Header.Set("apns-topic", message.Header.Topic)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/bitly/nsq/nsqd"
	"github.com/bitly/nsq/util"
	"github.com/mreiferson/go-options"
)

type tlsRequiredOption int

func (t *tlsRequiredOption) Set(s string) error {
	s = strings.ToLower(s)
	if s == "tcp-https" {
		*t = nsqd.TLSRequiredExceptHTTP
		return nil
	}
	required, err := strconv.ParseBool(s)
	if required {
		*t = nsqd.TLSRequired
	} else {
		*t = nsqd.TLSNotRequired
	}
	return err
}

func (t *tlsRequiredOption) Get() interface{} { return *t }

func (t *tlsRequiredOption) String() string {
	return strconv.FormatInt(int64(*t), 10)
}

func (t *tlsRequiredOption) IsBoolFlag() bool { return true }

type tlsVersionOption uint16

func (t *tlsVersionOption) Set(s string) error {
	s = strings.ToLower(s)
	switch s {
	case "":
		return nil
	case "ssl3.0":
		*t = tls.VersionSSL30
	case "tls1.0":
		*t = tls.VersionTLS10
	case "tls1.1":
		*t = tls.VersionTLS11
	case "tls1.2":
		*t = tls.VersionTLS12
	default:
		return fmt.Errorf("unknown tlsVersionOption %q", s)
	}
	return nil
}

func (t *tlsVersionOption) Get() interface{} {
	return *t
}

func (t *tlsVersionOption) String() string {
	return strconv.FormatInt(int64(*t), 10)
}

func nsqFlagset() *flag.FlagSet {
	flagSet := flag.NewFlagSet("nsqd", flag.ExitOnError)

	// basic options
	flagSet.String("config", "", "path to config file")
	flagSet.Bool("version", false, "print version string")
	flagSet.Bool("verbose", false, "enable verbose logging")
	flagSet.Int64("worker-id", 0, "unique seed for message ID generation (int) in range [0,4096) (will default to a hash of hostname)")
	flagSet.String("https-address", "", "<addr>:<port> to listen on for HTTPS clients")
	flagSet.String("http-address", "0.0.0.0:4151", "<addr>:<port> to listen on for HTTP clients")
	flagSet.String("tcp-address", "0.0.0.0:4150", "<addr>:<port> to listen on for TCP clients")

	authHttpAddresses := util.StringArray{}
	flagSet.Var(&authHttpAddresses, "auth-http-address", "<addr>:<port> to query auth server (may be given multiple times)")

	flagSet.String("broadcast-address", "", "address that will be registered with lookupd (defaults to the OS hostname)")
	lookupdTCPAddrs := util.StringArray{}
	flagSet.Var(&lookupdTCPAddrs, "lookupd-tcp-address", "lookupd TCP address (may be given multiple times)")

	// diskqueue options
	flagSet.String("data-path", "", "path to store disk-backed messages")
	flagSet.Int64("mem-queue-size", 10000, "number of messages to keep in memory (per topic/channel)")
	flagSet.Int64("max-bytes-per-file", 104857600, "number of bytes per diskqueue file before rolling")
	flagSet.Int64("sync-every", 2500, "number of messages per diskqueue fsync")
	flagSet.Duration("sync-timeout", 2*time.Second, "duration of time per diskqueue fsync")

	// msg and command options
	flagSet.String("msg-timeout", "60s", "duration to wait before auto-requeing a message")
	flagSet.Duration("max-msg-timeout", 15*time.Minute, "maximum duration before a message will timeout")
	flagSet.Int64("max-msg-size", 1024768, "maximum size of a single message in bytes")
	flagSet.Duration("max-req-timeout", 1*time.Hour, "maximum requeuing timeout for a message")
	// remove, deprecated
	flagSet.Int64("max-message-size", 1024768, "(deprecated use --max-msg-size) maximum size of a single message in bytes")
	flagSet.Int64("max-body-size", 5*1024768, "maximum size of a single command body")

	// client overridable configuration options
	flagSet.Duration("max-heartbeat-interval", 60*time.Second, "maximum client configurable duration of time between client heartbeats")
	flagSet.Int64("max-rdy-count", 2500, "maximum RDY count for a client")
	flagSet.Int64("max-output-buffer-size", 64*1024, "maximum client configurable size (in bytes) for a client output buffer")
	flagSet.Duration("max-output-buffer-timeout", 1*time.Second, "maximum client configurable duration of time between flushing to a client")

	// statsd integration options
	flagSet.String("statsd-address", "", "UDP <addr>:<port> of a statsd daemon for pushing stats")
	flagSet.String("statsd-interval", "60s", "duration between pushing to statsd")
	flagSet.Bool("statsd-mem-stats", true, "toggle sending memory and GC stats to statsd")
	flagSet.String("statsd-prefix", "nsq.%s", "prefix used for keys sent to statsd (%s for host replacement)")

	// End to end percentile flags
	e2eProcessingLatencyPercentiles := util.FloatArray{}
	flagSet.Var(&e2eProcessingLatencyPercentiles, "e2e-processing-latency-percentile", "message processing time percentiles to keep track of (can be specified multiple times or comma separated, default none)")
	flagSet.Duration("e2e-processing-latency-window-time", 10*time.Minute, "calculate end to end latency quantiles for this duration of time (ie: 60s would only show quantile calculations from the past 60 seconds)")

	// TLS config
	flagSet.String("tls-cert", "", "path to certificate file")
	flagSet.String("tls-key", "", "path to private key file")
	flagSet.String("tls-client-auth-policy", "", "client certificate auth policy ('require' or 'require-verify')")
	flagSet.String("tls-root-ca-file", "", "path to private certificate authority pem")
	var tlsRequired tlsRequiredOption
	var tlsMinVersion tlsVersionOption
	flagSet.Var(&tlsRequired, "tls-required", "require TLS for client connections (true, false, tcp-https)")
	flagSet.Var(&tlsMinVersion, "tls-min-version", "minimum SSL/TLS version acceptable ('ssl3.0', 'tls1.0', 'tls1.1', or 'tls1.2')")

	// compression
	flagSet.Bool("deflate", true, "enable deflate feature negotiation (client compression)")
	flagSet.Int("max-deflate-level", 6, "max deflate compression level a client can negotiate (> values == > nsqd CPU usage)")
	flagSet.Bool("snappy", true, "enable snappy feature negotiation (client compression)")

	return flagSet
}

type config map[string]interface{}

// Validate settings in the config file, and fatal on errors
func (cfg config) Validate() {
	// special validation/translation
	if v, exists := cfg["tls_required"]; exists {
		var t tlsRequiredOption
		err := t.Set(fmt.Sprintf("%v", v))
		if err == nil {
			cfg["tls_required"] = t.String()
		} else {
			log.Fatalf("ERROR: failed parsing tls required %v", v)
		}
	}
	if v, exists := cfg["tls_min_version"]; exists {
		var t tlsVersionOption
		err := t.Set(fmt.Sprintf("%v", v))
		if err == nil {
			newVal := fmt.Sprintf("%v", t.Get())
			if newVal != "0" {
				cfg["tls_min_version"] = newVal
			} else {
				delete(cfg, "tls_min_version")
			}
		} else {
			log.Fatalf("ERROR: failed parsing tls min version %v", v)
		}
	}
}

func main() {

	flagSet := nsqFlagset()
	flagSet.Parse(os.Args[1:])

	rand.Seed(time.Now().UTC().UnixNano())

	if flagSet.Lookup("version").Value.(flag.Getter).Get().(bool) {
		fmt.Println(util.Version("nsqd"))
		return
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	var cfg config
	configFile := flagSet.Lookup("config").Value.String()
	if configFile != "" {
		_, err := toml.DecodeFile(configFile, &cfg)
		if err != nil {
			log.Fatalf("ERROR: failed to load config file %s - %s", configFile, err.Error())
		}
	}
	cfg.Validate()

	opts := nsqd.NewNSQDOptions()
	options.Resolve(opts, flagSet, cfg)
	nsqd := nsqd.NewNSQD(opts)

	nsqd.LoadMetadata()
	err := nsqd.PersistMetadata()
	if err != nil {
		log.Fatalf("ERROR: failed to persist metadata - %s", err.Error())
	}
	nsqd.Main()
	<-signalChan
	nsqd.Exit()
}
<commit_msg>Verbose option<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/bitly/nsq/nsqd"
	"github.com/bitly/nsq/util"
	"github.com/mreiferson/go-options"
)

type tlsRequiredOption int

func (t *tlsRequiredOption) Set(s string) error {
	s = strings.ToLower(s)
	if s == "tcp-https" {
		*t = nsqd.TLSRequiredExceptHTTP
		return nil
	}
	required, err := strconv.ParseBool(s)
	if required {
		*t = nsqd.TLSRequired
	} else {
		*t = nsqd.TLSNotRequired
	}
	return err
}

func (t *tlsRequiredOption) Get() interface{} { return *t }

func (t *tlsRequiredOption) String() string {
	return strconv.FormatInt(int64(*t), 10)
}

func (t *tlsRequiredOption) IsBoolFlag() bool { return true }

type tlsVersionOption uint16

func (t *tlsVersionOption) Set(s string) error {
	s = strings.ToLower(s)
	switch s {
	case "":
		return nil
	case "ssl3.0":
		*t = tls.VersionSSL30
	case "tls1.0":
		*t = tls.VersionTLS10
	case "tls1.1":
		*t = tls.VersionTLS11
	case "tls1.2":
		*t = tls.VersionTLS12
	default:
		return fmt.Errorf("unknown tlsVersionOption %q", s)
	}
	return nil
}

func (t *tlsVersionOption) Get() interface{} {
	return *t
}

func (t *tlsVersionOption) String() string {
	return strconv.FormatInt(int64(*t), 10)
}

func nsqFlagset() *flag.FlagSet {
	flagSet := flag.NewFlagSet("nsqd", flag.ExitOnError)

	// basic options
	flagSet.String("config", "", "path to config file")
	flagSet.Bool("version", false, "print version string")
	flagSet.Bool("verbose", false, "enable verbose logging")
	flagSet.Bool("quiet", false, "disable annoyed logging")
	flagSet.Int64("worker-id", 0, "unique seed for message ID generation (int) in range [0,4096) (will default to a hash of hostname)")
	flagSet.String("https-address", "", "<addr>:<port> to listen on for HTTPS clients")
	flagSet.String("http-address", "0.0.0.0:4151", "<addr>:<port> to listen on for HTTP clients")
	flagSet.String("tcp-address", "0.0.0.0:4150", "<addr>:<port> to listen on for TCP clients")

	authHttpAddresses := util.StringArray{}
	flagSet.Var(&authHttpAddresses, "auth-http-address", "<addr>:<port> to query auth server (may be given multiple times)")

	flagSet.String("broadcast-address", "", "address that will be registered with lookupd (defaults to the OS hostname)")
	lookupdTCPAddrs := util.StringArray{}
	flagSet.Var(&lookupdTCPAddrs, "lookupd-tcp-address", "lookupd TCP address (may be given multiple times)")

	// diskqueue options
	flagSet.String("data-path", "", "path to store disk-backed messages")
	flagSet.Int64("mem-queue-size", 10000, "number of messages to keep in memory (per topic/channel)")
	flagSet.Int64("max-bytes-per-file", 104857600, "number of bytes per diskqueue file before rolling")
	flagSet.Int64("sync-every", 2500, "number of messages per diskqueue fsync")
	flagSet.Duration("sync-timeout", 2*time.Second, "duration of time per diskqueue fsync")

	// msg and command options
	flagSet.String("msg-timeout", "60s", "duration to wait before auto-requeing a message")
	flagSet.Duration("max-msg-timeout", 15*time.Minute, "maximum duration before a message will timeout")
	flagSet.Int64("max-msg-size", 1024768, "maximum size of a single message in bytes")
	flagSet.Duration("max-req-timeout", 1*time.Hour, "maximum requeuing timeout for a message")
	// remove, deprecated
	flagSet.Int64("max-message-size", 1024768, "(deprecated use --max-msg-size) maximum size of a single message in bytes")
	flagSet.Int64("max-body-size", 5*1024768, "maximum size of a single command body")

	// client overridable configuration options
	flagSet.Duration("max-heartbeat-interval", 60*time.Second, "maximum client configurable duration of time between client heartbeats")
	flagSet.Int64("max-rdy-count", 2500, "maximum RDY count for a client")
	flagSet.Int64("max-output-buffer-size", 64*1024, "maximum client configurable size (in bytes) for a client output buffer")
	flagSet.Duration("max-output-buffer-timeout", 1*time.Second, "maximum client configurable duration of time between flushing to a client")

	// statsd integration options
	flagSet.String("statsd-address", "", "UDP <addr>:<port> of a statsd daemon for pushing stats")
	flagSet.String("statsd-interval", "60s", "duration between pushing to statsd")
	flagSet.Bool("statsd-mem-stats", true, "toggle sending memory and GC stats to statsd")
	flagSet.String("statsd-prefix", "nsq.%s", "prefix used for keys sent to statsd (%s for host replacement)")

	// End to end percentile flags
	e2eProcessingLatencyPercentiles := util.FloatArray{}
	flagSet.Var(&e2eProcessingLatencyPercentiles, "e2e-processing-latency-percentile", "message processing time percentiles to keep track of (can be specified multiple times or comma separated, default none)")
	flagSet.Duration("e2e-processing-latency-window-time", 10*time.Minute, "calculate end to end latency quantiles for this duration of time (ie: 60s would only show quantile calculations from the past 60 seconds)")

	// TLS config
	flagSet.String("tls-cert", "", "path to certificate file")
	flagSet.String("tls-key", "", "path to private key file")
	flagSet.String("tls-client-auth-policy", "", "client certificate auth policy ('require' or 'require-verify')")
	flagSet.String("tls-root-ca-file", "", "path to private certificate authority pem")
	var tlsRequired tlsRequiredOption
	var tlsMinVersion tlsVersionOption
	flagSet.Var(&tlsRequired, "tls-required", "require TLS for client connections (true, false, tcp-https)")
	flagSet.Var(&tlsMinVersion, "tls-min-version", "minimum SSL/TLS version acceptable ('ssl3.0', 'tls1.0', 'tls1.1', or 'tls1.2')")

	// compression
	flagSet.Bool("deflate", true, "enable deflate feature negotiation (client compression)")
	flagSet.Int("max-deflate-level", 6, "max deflate compression level a client can negotiate (> values == > nsqd CPU usage)")
	flagSet.Bool("snappy", true, "enable snappy feature negotiation (client compression)")

	return flagSet
}

type config map[string]interface{}

// Validate settings in the config file, and fatal on errors
func (cfg config) Validate() {
	// special validation/translation
	if v, exists := cfg["tls_required"]; exists {
		var t tlsRequiredOption
		err := t.Set(fmt.Sprintf("%v", v))
		if err == nil {
			cfg["tls_required"] = t.String()
		} else {
			log.Fatalf("ERROR: failed parsing tls required %v", v)
		}
	}
	if v, exists := cfg["tls_min_version"]; exists {
		var t tlsVersionOption
		err := t.Set(fmt.Sprintf("%v", v))
		if err == nil {
			newVal := fmt.Sprintf("%v", t.Get())
			if newVal != "0" {
				cfg["tls_min_version"] = newVal
			} else {
				delete(cfg, "tls_min_version")
			}
		} else {
			log.Fatalf("ERROR: failed parsing tls min version %v", v)
		}
	}
}

func main() {

	flagSet := nsqFlagset()
	flagSet.Parse(os.Args[1:])

	rand.Seed(time.Now().UTC().UnixNano())

	if flagSet.Lookup("version").Value.(flag.Getter).Get().(bool) {
		fmt.Println(util.Version("nsqd"))
		return
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	var cfg config
	configFile := flagSet.Lookup("config").Value.String()
	if configFile != "" {
		_, err := toml.DecodeFile(configFile, &cfg)
		if err != nil {
			log.Fatalf("ERROR: failed to load config file %s - %s", configFile, err.Error())
		}
	}
	cfg.Validate()

	opts := nsqd.NewNSQDOptions()
	options.Resolve(opts, flagSet, cfg)
	nsqd := nsqd.NewNSQD(opts)

	nsqd.LoadMetadata()
	err := nsqd.PersistMetadata()
	if err != nil {
		log.Fatalf("ERROR: failed to persist metadata - %s", err.Error())
	}
	nsqd.Main()
	<-signalChan
	nsqd.Exit()
}
<|endoftext|>


<commit_before>// Copyright 2018 The Terraformer 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 main

import (
	"log"
	"os"
	//"os/exec"
	"sort"

	"github.com/GoogleCloudPlatform/terraformer/cmd"
	aws_terraforming "github.com/GoogleCloudPlatform/terraformer/providers/aws"
	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
)

const command = "terraform init && terraform plan"

func main() {
	//region := "ap-southeast-1"
	profile := "personal"
	services := []string{}
	provider := &aws_terraforming.AWSProvider{}
	for service := range provider.GetSupportedService() {
		if service == "route53" {
			continue
		}
		if service == "iam" {
			continue
		}
		if service == "sg" {
			continue
		}
		services = append(services, service)

	}
	services = []string{"cloudfront"}
	sort.Strings(services)
	provider = &aws_terraforming.AWSProvider{
		Provider: terraform_utils.Provider{},
	}
	err := cmd.Import(provider, cmd.ImportOptions{
		Resources:   services,
		PathPattern: cmd.DefaultPathPattern,
		PathOutput:  cmd.DefaultPathOutput,
		State:       "local",
		Connect:     true,
	}, []string{"", profile})
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
	//rootPath, _ := os.Getwd()
	//for _, serviceName := range services {
	//	currentPath := cmd.Path(cmd.DefaultPathPattern, provider.GetName(), serviceName, cmd.DefaultPathOutput)
	//	if err := os.Chdir(currentPath); err != nil {
	//		log.Println(err)
	//		os.Exit(1)
	//	}
	//	cmd := exec.Command("sh", "-c", command)
	//	cmd.Stdout = os.Stdout
	//	cmd.Stderr = os.Stderr
	//	err = cmd.Run()
	//	if err != nil {
	//		log.Println(err)
	//		os.Exit(1)
	//	}
	//	os.Chdir(rootPath)
	//}
}
<commit_msg>adjusted AWS test<commit_after>// Copyright 2018 The Terraformer 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 main

import (
	"log"
	"os"
	"os/exec"
	"sort"

	"github.com/GoogleCloudPlatform/terraformer/cmd"
	aws_terraforming "github.com/GoogleCloudPlatform/terraformer/providers/aws"
	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
)

const command = "terraform init && terraform plan"

func main() {
	region := "ap-southeast-1"
	profile := "personal"
	services := []string{}
	provider := &aws_terraforming.AWSProvider{}
	for service := range provider.GetSupportedService() {
		if service == "route53" {
			continue
		}
		if service == "iam" {
			continue
		}
		if service == "sg" {
			continue
		}
		services = append(services, service)

	}
	services = []string{"vpc", "subnet", "nacl"}
	sort.Strings(services)
	provider = &aws_terraforming.AWSProvider{
		Provider: terraform_utils.Provider{},
	}
	err := cmd.Import(provider, cmd.ImportOptions{
		Resources:   services,
		PathPattern: cmd.DefaultPathPattern,
		PathOutput:  cmd.DefaultPathOutput,
		State:       "local",
		Connect:     true,
	}, []string{region, profile})
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
	rootPath, _ := os.Getwd()
	for _, serviceName := range services {
		currentPath := cmd.Path(cmd.DefaultPathPattern, provider.GetName(), serviceName, cmd.DefaultPathOutput)
		if err := os.Chdir(currentPath); err != nil {
			log.Println(err)
			os.Exit(1)
		}
		cmd := exec.Command("sh", "-c", command)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			log.Println(err)
			os.Exit(1)
		}
		os.Chdir(rootPath)
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build dragonfly freebsd linux nacl netbsd openbsd solaris

package x509

import "io/ioutil"

// Possible certificate files; stop after finding one.
var certFiles = []string{
	"/etc/ssl/certs/ca-certificates.crt",     // Debian/Ubuntu/Gentoo etc.
	"/etc/pki/tls/certs/ca-bundle.crt",       // Fedora/RHEL
	"/etc/ssl/ca-bundle.pem",                 // OpenSUSE
	"/etc/ssl/cert.pem",                      // OpenBSD
	"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
}

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

func initSystemRoots() {
	roots := NewCertPool()
	for _, file := range certFiles {
		data, err := ioutil.ReadFile(file)
		if err == nil {
			roots.AppendCertsFromPEM(data)
			systemRoots = roots
			return
		}
	}

	// All of the files failed to load. systemRoots will be nil which will
	// trigger a specific error at verification time.
}
<commit_msg>x509: add root certs for android.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build dragonfly freebsd linux nacl netbsd openbsd solaris

package x509

import "io/ioutil"

// Possible certificate files; stop after finding one.
var certFiles = []string{
	"/etc/ssl/certs/ca-certificates.crt",     // Debian/Ubuntu/Gentoo etc.
	"/etc/pki/tls/certs/ca-bundle.crt",       // Fedora/RHEL
	"/etc/ssl/ca-bundle.pem",                 // OpenSUSE
	"/etc/ssl/cert.pem",                      // OpenBSD
	"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
}

// Possible directories with certificate files; stop after successfully
// reading at least one file from a directory.
var certDirectories = []string{
	"/system/etc/security/cacerts", // Android

}

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

func initSystemRoots() {
	roots := NewCertPool()
	for _, file := range certFiles {
		data, err := ioutil.ReadFile(file)
		if err == nil {
			roots.AppendCertsFromPEM(data)
			systemRoots = roots
			return
		}
	}

	for _, directory := range certDirectories {
		fis, err := ioutil.ReadDir(directory)
		if err != nil {
			continue
		}
		rootsAdded := false
		for _, fi := range fis {
			data, err := ioutil.ReadFile(directory + "/" + fi.Name())
			if err == nil && roots.AppendCertsFromPEM(data) {
				rootsAdded = true
			}
		}
		if rootsAdded {
			systemRoots = roots
			return
		}
	}

	// All of the files failed to load. systemRoots will be nil which will
	// trigger a specific error at verification time.
}
<|endoftext|>


<commit_before>package appstore

import "encoding/json"

type numericString string

func (n *numericString) UnmarshalJSON(b []byte) error {
	var number json.Number
	if err := json.Unmarshal(b, &number); err != nil {
		return err
	}
	*n = numericString(number.String())
	return nil
}

type Environment string

const (
	Sandbox    Environment = "Sandbox"
	Production Environment = "Production"
)

type (
	// https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
	// The IAPRequest type has the request parameter
	IAPRequest struct {
		ReceiptData string `json:"receipt-data"`
		// Only used for receipts that contain auto-renewable subscriptions.
		Password string `json:"password,omitempty"`
		// Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions.
		// If value is true, response includes only the latest renewal transaction for any subscriptions.
		ExcludeOldTransactions bool `json:"exclude-old-transactions"`
	}

	// The ReceiptCreationDate type indicates the date when the app receipt was created.
	ReceiptCreationDate struct {
		CreationDate    string `json:"receipt_creation_date"`
		CreationDateMS  string `json:"receipt_creation_date_ms"`
		CreationDatePST string `json:"receipt_creation_date_pst"`
	}

	// The RequestDate type indicates the date and time that the request was sent
	RequestDate struct {
		RequestDate    string `json:"request_date"`
		RequestDateMS  string `json:"request_date_ms"`
		RequestDatePST string `json:"request_date_pst"`
	}

	// The PurchaseDate type indicates the date and time that the item was purchased
	PurchaseDate struct {
		PurchaseDate    string `json:"purchase_date"`
		PurchaseDateMS  string `json:"purchase_date_ms"`
		PurchaseDatePST string `json:"purchase_date_pst"`
	}

	// The OriginalPurchaseDate type indicates the beginning of the subscription period
	OriginalPurchaseDate struct {
		OriginalPurchaseDate    string `json:"original_purchase_date"`
		OriginalPurchaseDateMS  string `json:"original_purchase_date_ms"`
		OriginalPurchaseDatePST string `json:"original_purchase_date_pst"`
	}

	// The ExpiresDate type indicates the expiration date for the subscription
	ExpiresDate struct {
		ExpiresDate             string `json:"expires_date,omitempty"`
		ExpiresDateMS           string `json:"expires_date_ms,omitempty"`
		ExpiresDatePST          string `json:"expires_date_pst,omitempty"`
		ExpiresDateFormatted    string `json:"expires_date_formatted,omitempty"`
		ExpiresDateFormattedPST string `json:"expires_date_formatted_pst,omitempty"`
	}

	// The CancellationDate type indicates the time and date of the cancellation by Apple customer support
	CancellationDate struct {
		CancellationDate    string `json:"cancellation_date,omitempty"`
		CancellationDateMS  string `json:"cancellation_date_ms,omitempty"`
		CancellationDatePST string `json:"cancellation_date_pst,omitempty"`
	}

	// The InApp type has the receipt attributes
	InApp struct {
		Quantity              string `json:"quantity"`
		ProductID             string `json:"product_id"`
		TransactionID         string `json:"transaction_id"`
		OriginalTransactionID string `json:"original_transaction_id"`
		WebOrderLineItemID    string `json:"web_order_line_item_id,omitempty"`

		IsTrialPeriod string `json:"is_trial_period"`
		ExpiresDate

		PurchaseDate
		OriginalPurchaseDate

		CancellationDate
		CancellationReason string `json:"cancellation_reason,omitempty"`
	}

	// The Receipt type has whole data of receipt
	Receipt struct {
		ReceiptType                string        `json:"receipt_type"`
		AdamID                     int64         `json:"adam_id"`
		AppItemID                  numericString `json:"app_item_id"`
		BundleID                   string        `json:"bundle_id"`
		ApplicationVersion         string        `json:"application_version"`
		DownloadID                 int64         `json:"download_id"`
		VersionExternalIdentifier  numericString `json:"version_external_identifier"`
		OriginalApplicationVersion string        `json:"original_application_version"`
		InApp                      []InApp       `json:"in_app"`
		ReceiptCreationDate
		RequestDate
		OriginalPurchaseDate
	}

	// A pending renewal may refer to a renewal that is scheduled in the future or a renewal that failed in the past for some reason.
	PendingRenewalInfo struct {
		SubscriptionExpirationIntent   string `json:"expiration_intent"`
		SubscriptionAutoRenewProductID string `json:"auto_renew_product_id"`
		SubscriptionRetryFlag          string `json:"is_in_billing_retry_period"`
		SubscriptionAutoRenewStatus    string `json:"auto_renew_status"`
		SubscriptionPriceConsentStatus string `json:"price_consent_status"`
		ProductID                      string `json:"product_id"`
	}

	// The IAPResponse type has the response properties
	// We defined each field by the current IAP response, but some fields are not mentioned
	// in the following Apple's document;
	// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
	// If you get other types or fields from the IAP response, you should use the struct you defined.
	IAPResponse struct {
		Status             int                  `json:"status"`
		Environment        Environment          `json:"environment"`
		Receipt            Receipt              `json:"receipt"`
		LatestReceiptInfo  []InApp              `json:"latest_receipt_info,omitempty"`
		LatestReceipt      string               `json:"latest_receipt,omitempty"`
		PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
		IsRetryable        bool                 `json:"is-retryable,omitempty"`
	}

	// The HttpStatusResponse struct contains the status code returned by the store
	// Used as a workaround to detect when to hit the production appstore or sandbox appstore regardless of receipt type
	StatusResponse struct {
		Status int `json:"status"`
	}

	// IAPResponseForIOS6 is iOS 6 style receipt schema.
	IAPResponseForIOS6 struct {
		AutoRenewProductID     string         `json:"auto_renew_product_id"`
		AutoRenewStatus        int            `json:"auto_renew_status"`
		ExpirationIntent       string         `json:"expiration_intent"`
		ISInBillingRetryPeriod string         `json:"is_in_billing_retry_period"`
		LatestReceiptInfo      ReceiptForIOS6 `json:"latest_expired_receipt_info"`
		Receipt                ReceiptForIOS6 `json:"receipt"`
		Status                 int            `json:"status"`
	}

	ReceiptForIOS6 struct {
		AppItemID numericString `json:"app_item_id"`
		BID       string        `json:"bid"`
		BVRS      string        `json:"bvrs"`
		ExpiresDate
		IsTrialPeriod        string `json:"is_trial_period"`
		IsInIntroOfferPeriod string `json:"is_in_intro_offer_period"`
		ItemID               string `json:"item_id"`
		ProductID            string `json:"product_id"`
		PurchaseDate
		OriginalTransactionID string `json:"original_transaction_id"`
		OriginalPurchaseDate
		Quantity                  string        `json:"quantity"`
		TransactionID             string        `json:"transaction_id"`
		UniqueIdentifier          string        `json:"unique_identifier"`
		UniqueVendorIdentifier    string        `json:"unique_vendor_identifier"`
		VersionExternalIdentifier numericString `json:"version_external_identifier,omitempty"`
		WebOrderLineItemID        string        `json:"web_order_line_item_id"`
	}
)
<commit_msg>Added cancellation date fields<commit_after>package appstore

import "encoding/json"

type numericString string

func (n *numericString) UnmarshalJSON(b []byte) error {
	var number json.Number
	if err := json.Unmarshal(b, &number); err != nil {
		return err
	}
	*n = numericString(number.String())
	return nil
}

type Environment string

const (
	Sandbox    Environment = "Sandbox"
	Production Environment = "Production"
)

type (
	// https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
	// The IAPRequest type has the request parameter
	IAPRequest struct {
		ReceiptData string `json:"receipt-data"`
		// Only used for receipts that contain auto-renewable subscriptions.
		Password string `json:"password,omitempty"`
		// Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions.
		// If value is true, response includes only the latest renewal transaction for any subscriptions.
		ExcludeOldTransactions bool `json:"exclude-old-transactions"`
	}

	// The ReceiptCreationDate type indicates the date when the app receipt was created.
	ReceiptCreationDate struct {
		CreationDate    string `json:"receipt_creation_date"`
		CreationDateMS  string `json:"receipt_creation_date_ms"`
		CreationDatePST string `json:"receipt_creation_date_pst"`
	}

	// The RequestDate type indicates the date and time that the request was sent
	RequestDate struct {
		RequestDate    string `json:"request_date"`
		RequestDateMS  string `json:"request_date_ms"`
		RequestDatePST string `json:"request_date_pst"`
	}

	// The PurchaseDate type indicates the date and time that the item was purchased
	PurchaseDate struct {
		PurchaseDate    string `json:"purchase_date"`
		PurchaseDateMS  string `json:"purchase_date_ms"`
		PurchaseDatePST string `json:"purchase_date_pst"`
	}

	// The OriginalPurchaseDate type indicates the beginning of the subscription period
	OriginalPurchaseDate struct {
		OriginalPurchaseDate    string `json:"original_purchase_date"`
		OriginalPurchaseDateMS  string `json:"original_purchase_date_ms"`
		OriginalPurchaseDatePST string `json:"original_purchase_date_pst"`
	}

	// The ExpiresDate type indicates the expiration date for the subscription
	ExpiresDate struct {
		ExpiresDate             string `json:"expires_date,omitempty"`
		ExpiresDateMS           string `json:"expires_date_ms,omitempty"`
		ExpiresDatePST          string `json:"expires_date_pst,omitempty"`
		ExpiresDateFormatted    string `json:"expires_date_formatted,omitempty"`
		ExpiresDateFormattedPST string `json:"expires_date_formatted_pst,omitempty"`
	}

	// The CancellationDate type indicates the time and date of the cancellation by Apple customer support
	CancellationDate struct {
		CancellationDate    string `json:"cancellation_date,omitempty"`
		CancellationDateMS  string `json:"cancellation_date_ms,omitempty"`
		CancellationDatePST string `json:"cancellation_date_pst,omitempty"`
	}

	// The InApp type has the receipt attributes
	InApp struct {
		Quantity              string `json:"quantity"`
		ProductID             string `json:"product_id"`
		TransactionID         string `json:"transaction_id"`
		OriginalTransactionID string `json:"original_transaction_id"`
		WebOrderLineItemID    string `json:"web_order_line_item_id,omitempty"`

		IsTrialPeriod string `json:"is_trial_period"`
		ExpiresDate

		PurchaseDate
		OriginalPurchaseDate

		CancellationDate
		CancellationReason string `json:"cancellation_reason,omitempty"`
	}

	// The Receipt type has whole data of receipt
	Receipt struct {
		ReceiptType                string        `json:"receipt_type"`
		AdamID                     int64         `json:"adam_id"`
		AppItemID                  numericString `json:"app_item_id"`
		BundleID                   string        `json:"bundle_id"`
		ApplicationVersion         string        `json:"application_version"`
		DownloadID                 int64         `json:"download_id"`
		VersionExternalIdentifier  numericString `json:"version_external_identifier"`
		OriginalApplicationVersion string        `json:"original_application_version"`
		InApp                      []InApp       `json:"in_app"`
		ReceiptCreationDate
		RequestDate
		OriginalPurchaseDate
	}

	// A pending renewal may refer to a renewal that is scheduled in the future or a renewal that failed in the past for some reason.
	PendingRenewalInfo struct {
		SubscriptionExpirationIntent   string `json:"expiration_intent"`
		SubscriptionAutoRenewProductID string `json:"auto_renew_product_id"`
		SubscriptionRetryFlag          string `json:"is_in_billing_retry_period"`
		SubscriptionAutoRenewStatus    string `json:"auto_renew_status"`
		SubscriptionPriceConsentStatus string `json:"price_consent_status"`
		ProductID                      string `json:"product_id"`
	}

	// The IAPResponse type has the response properties
	// We defined each field by the current IAP response, but some fields are not mentioned
	// in the following Apple's document;
	// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
	// If you get other types or fields from the IAP response, you should use the struct you defined.
	IAPResponse struct {
		Status             int                  `json:"status"`
		Environment        Environment          `json:"environment"`
		Receipt            Receipt              `json:"receipt"`
		LatestReceiptInfo  []InApp              `json:"latest_receipt_info,omitempty"`
		LatestReceipt      string               `json:"latest_receipt,omitempty"`
		PendingRenewalInfo []PendingRenewalInfo `json:"pending_renewal_info,omitempty"`
		IsRetryable        bool                 `json:"is-retryable,omitempty"`
	}

	// The HttpStatusResponse struct contains the status code returned by the store
	// Used as a workaround to detect when to hit the production appstore or sandbox appstore regardless of receipt type
	StatusResponse struct {
		Status int `json:"status"`
	}

	// IAPResponseForIOS6 is iOS 6 style receipt schema.
	IAPResponseForIOS6 struct {
		AutoRenewProductID     string         `json:"auto_renew_product_id"`
		AutoRenewStatus        int            `json:"auto_renew_status"`
		CancellationReason     string         `json:"cancellation_reason,omitempty"`
		ExpirationIntent       string         `json:"expiration_intent,omitempty"`
		IsInBillingRetryPeriod string         `json:"is_in_billing_retry_period,omitempty"`
		LatestReceiptInfo      ReceiptForIOS6 `json:"latest_expired_receipt_info"`
		Receipt                ReceiptForIOS6 `json:"receipt"`
		Status                 int            `json:"status"`
	}

	ReceiptForIOS6 struct {
		AppItemID numericString `json:"app_item_id"`
		BID       string        `json:"bid"`
		BVRS      string        `json:"bvrs"`
		CancellationDate
		ExpiresDate
		IsTrialPeriod        string `json:"is_trial_period"`
		IsInIntroOfferPeriod string `json:"is_in_intro_offer_period"`
		ItemID               string `json:"item_id"`
		ProductID            string `json:"product_id"`
		PurchaseDate
		OriginalTransactionID string `json:"original_transaction_id"`
		OriginalPurchaseDate
		Quantity                  string        `json:"quantity"`
		TransactionID             string        `json:"transaction_id"`
		UniqueIdentifier          string        `json:"unique_identifier"`
		UniqueVendorIdentifier    string        `json:"unique_vendor_identifier"`
		VersionExternalIdentifier numericString `json:"version_external_identifier,omitempty"`
		WebOrderLineItemID        string        `json:"web_order_line_item_id"`
	}
)
<|endoftext|>


<commit_before>package sisito

import (
	"bytes"
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"gopkg.in/gorp.v1"
	"log"
	"os"
	"time"
)

type Driver struct {
	Dbmap *gorp.DbMap
}

func NewDriver(config *Config, debug bool) (driver *Driver, err error) {
	url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true",
		config.Database.Username,
		config.Database.Password,
		config.Database.Host,
		config.Database.Port,
		config.Database.Database)

	var db *sql.DB
	db, err = sql.Open("mysql", url)

	if err != nil {
		return
	}

	dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{}}

	if debug {
		dbmap.TraceOn("[gorp]", log.New(os.Stdout, "", log.Lmicroseconds))
	}

	driver = &Driver{Dbmap: dbmap}

	return
}

func (driver *Driver) Close() {
	driver.Close()
}

type BounceMail struct {
	Id             int32
	Timestamp      time.Time
	Lhost          string
	Rhost          string
	Alias          string
	Listid         string
	Reason         string
	Action         string
	Subject        string
	Messageid      string
	Smtpagent      string
	Softbounce     uint8
	Smtpcommand    string
	Destination    string
	Senderdomain   string
	Feedbacktype   string
	Diagnosticcode string
	Deliverystatus string
	Timezoneoffset string
	Addresser      string
	Recipient      string
	Digest         string
	CreatedAt      time.Time `db:"created_at"`
	UpdatedAt      time.Time `db:"updated_at"`
	Whitelisted    uint8
}

type Recipient struct {
	Recipient string
}

func (driver *Driver) recentlyBounced(name string, value string, senderdomain string) (bounced []BounceMail, err error) {
	sqlBase := fmt.Sprintf(`
    SELECT bm.*, IF(wm.id IS NULL, 0, 1) AS whitelisted
      FROM bounce_mails bm LEFT JOIN whitelist_mails wm
        ON bm.recipient = wm.recipient AND bm.senderdomain = wm.senderdomain
     WHERE bm.%s = ?`, name)

	sqlBuf := bytes.NewBufferString(sqlBase)
	params := make([]interface{}, 1)
	params[0] = value

	if senderdomain != "" {
		sqlBuf.WriteString(`
       AND bm.senderdomain = ?`)

		params = append(params, senderdomain)
	}

	sqlBuf.WriteString(`
  ORDER BY bm.id DESC
     LIMIT 1`)

	sql := sqlBuf.String()

	bounced = []BounceMail{}
	_, err = driver.Dbmap.Select(&bounced, sql, params...)

	return
}

func (driver *Driver) isBounced(name string, value string, senderdomain string) (bounced bool, err error) {
	sqlBase := fmt.Sprintf(`
    SELECT 1
      FROM bounce_mails bm LEFT JOIN whitelist_mails wm
        ON bm.recipient = wm.recipient AND bm.senderdomain = wm.senderdomain
     WHERE bm.%s = ?`, name)

	sqlBuf := bytes.NewBufferString(sqlBase)
	params := make([]interface{}, 1)
	params[0] = value

	if senderdomain != "" {
		sqlBuf.WriteString(`
       AND bm.senderdomain = ?`)

		params = append(params, senderdomain)
	}

	sqlBuf.WriteString(`
       AND wm.id IS NULL
     LIMIT 1`)

	sql := sqlBuf.String()

	var count int64
	count, err = driver.Dbmap.SelectInt(sql, params...)

	if err != nil {
		return
	}

	if count > 0 {
		bounced = true
	} else {
		bounced = false
	}

	return
}

func (driver *Driver) blacklistRecipients(senderdomain string) (recipients []string, err error) {
	sqlBase := fmt.Sprintf(`
    SELECT bm.recipient
      FROM bounce_mails bm LEFT JOIN whitelist_mails wm
        ON bm.recipient = wm.recipient AND bm.senderdomain = wm.senderdomain
     WHERE wm.id IS NULL`)

	sqlBuf := bytes.NewBufferString(sqlBase)
	params := make([]interface{}, 0)

	if senderdomain != "" {
		sqlBuf.WriteString(`
       AND bm.senderdomain = ?`)

		params = append(params, senderdomain)
	}

	sqlBuf.WriteString(`
  GROUP BY recipient`)

	sql := sqlBuf.String()

	recipients = []string{}
	_, err = driver.Dbmap.Select(&recipients, sql, params...)

	if err != nil {
		return
	}

	return
}
<commit_msg>Remove unused code<commit_after>package sisito

import (
	"bytes"
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"gopkg.in/gorp.v1"
	"log"
	"os"
	"time"
)

type Driver struct {
	Dbmap *gorp.DbMap
}

func NewDriver(config *Config, debug bool) (driver *Driver, err error) {
	url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true",
		config.Database.Username,
		config.Database.Password,
		config.Database.Host,
		config.Database.Port,
		config.Database.Database)

	var db *sql.DB
	db, err = sql.Open("mysql", url)

	if err != nil {
		return
	}

	dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{}}

	if debug {
		dbmap.TraceOn("[gorp]", log.New(os.Stdout, "", log.Lmicroseconds))
	}

	driver = &Driver{Dbmap: dbmap}

	return
}

func (driver *Driver) Close() {
	driver.Close()
}

type BounceMail struct {
	Id             int32
	Timestamp      time.Time
	Lhost          string
	Rhost          string
	Alias          string
	Listid         string
	Reason         string
	Action         string
	Subject        string
	Messageid      string
	Smtpagent      string
	Softbounce     uint8
	Smtpcommand    string
	Destination    string
	Senderdomain   string
	Feedbacktype   string
	Diagnosticcode string
	Deliverystatus string
	Timezoneoffset string
	Addresser      string
	Recipient      string
	Digest         string
	CreatedAt      time.Time `db:"created_at"`
	UpdatedAt      time.Time `db:"updated_at"`
	Whitelisted    uint8
}

func (driver *Driver) recentlyBounced(name string, value string, senderdomain string) (bounced []BounceMail, err error) {
	sqlBase := fmt.Sprintf(`
    SELECT bm.*, IF(wm.id IS NULL, 0, 1) AS whitelisted
      FROM bounce_mails bm LEFT JOIN whitelist_mails wm
        ON bm.recipient = wm.recipient AND bm.senderdomain = wm.senderdomain
     WHERE bm.%s = ?`, name)

	sqlBuf := bytes.NewBufferString(sqlBase)
	params := make([]interface{}, 1)
	params[0] = value

	if senderdomain != "" {
		sqlBuf.WriteString(`
       AND bm.senderdomain = ?`)

		params = append(params, senderdomain)
	}

	sqlBuf.WriteString(`
  ORDER BY bm.id DESC
     LIMIT 1`)

	sql := sqlBuf.String()

	bounced = []BounceMail{}
	_, err = driver.Dbmap.Select(&bounced, sql, params...)

	return
}

func (driver *Driver) isBounced(name string, value string, senderdomain string) (bounced bool, err error) {
	sqlBase := fmt.Sprintf(`
    SELECT 1
      FROM bounce_mails bm LEFT JOIN whitelist_mails wm
        ON bm.recipient = wm.recipient AND bm.senderdomain = wm.senderdomain
     WHERE bm.%s = ?`, name)

	sqlBuf := bytes.NewBufferString(sqlBase)
	params := make([]interface{}, 1)
	params[0] = value

	if senderdomain != "" {
		sqlBuf.WriteString(`
       AND bm.senderdomain = ?`)

		params = append(params, senderdomain)
	}

	sqlBuf.WriteString(`
       AND wm.id IS NULL
     LIMIT 1`)

	sql := sqlBuf.String()

	var count int64
	count, err = driver.Dbmap.SelectInt(sql, params...)

	if err != nil {
		return
	}

	if count > 0 {
		bounced = true
	} else {
		bounced = false
	}

	return
}

func (driver *Driver) blacklistRecipients(senderdomain string) (recipients []string, err error) {
	sqlBase := fmt.Sprintf(`
    SELECT bm.recipient
      FROM bounce_mails bm LEFT JOIN whitelist_mails wm
        ON bm.recipient = wm.recipient AND bm.senderdomain = wm.senderdomain
     WHERE wm.id IS NULL`)

	sqlBuf := bytes.NewBufferString(sqlBase)
	params := make([]interface{}, 0)

	if senderdomain != "" {
		sqlBuf.WriteString(`
       AND bm.senderdomain = ?`)

		params = append(params, senderdomain)
	}

	sqlBuf.WriteString(`
  GROUP BY recipient`)

	sql := sqlBuf.String()

	recipients = []string{}
	_, err = driver.Dbmap.Select(&recipients, sql, params...)

	if err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>// Copyright Istio 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 aggregate

import (
	"sort"
	"sync"

	"github.com/hashicorp/go-multierror"
	"go.uber.org/atomic"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/serviceregistry"
	"istio.io/istio/pilot/pkg/serviceregistry/provider"
	"istio.io/istio/pkg/cluster"
	"istio.io/istio/pkg/config/host"
	"istio.io/istio/pkg/config/labels"
	"istio.io/istio/pkg/config/mesh"
	"istio.io/istio/pkg/spiffe"
	"istio.io/pkg/log"
)

// The aggregate controller does not implement serviceregistry.Instance since it may be comprised of various
// providers and clusters.
var (
	_ model.ServiceDiscovery = &Controller{}
	_ model.Controller       = &Controller{}
)

// Controller aggregates data across different registries and monitors for changes
type Controller struct {
	registries []serviceregistry.Instance
	storeLock  sync.RWMutex
	meshHolder mesh.Holder
	running    *atomic.Bool
}

type Options struct {
	MeshHolder mesh.Holder
}

// NewController creates a new Aggregate controller
func NewController(opt Options) *Controller {
	return &Controller{
		registries: make([]serviceregistry.Instance, 0),
		meshHolder: opt.MeshHolder,
		running:    atomic.NewBool(false),
	}
}

// AddRegistry adds registries into the aggregated controller
func (c *Controller) AddRegistry(registry serviceregistry.Instance) {
	c.storeLock.Lock()
	defer c.storeLock.Unlock()

	c.registries = append(c.registries, registry)
}

// DeleteRegistry deletes specified registry from the aggregated controller
func (c *Controller) DeleteRegistry(clusterID cluster.ID, providerID provider.ID) {
	c.storeLock.Lock()
	defer c.storeLock.Unlock()

	if len(c.registries) == 0 {
		log.Warnf("Registry list is empty, nothing to delete")
		return
	}
	index, ok := c.getRegistryIndex(clusterID, providerID)
	if !ok {
		log.Warnf("Registry %s is not found in the registries list, nothing to delete", clusterID)
		return
	}
	c.registries = append(c.registries[:index], c.registries[index+1:]...)
	log.Infof("Registry for the cluster %s has been deleted.", clusterID)
}

// GetRegistries returns a copy of all registries
func (c *Controller) GetRegistries() []serviceregistry.Instance {
	c.storeLock.RLock()
	defer c.storeLock.RUnlock()

	// copy registries to prevent race, no need to deep copy here.
	out := make([]serviceregistry.Instance, len(c.registries))
	for i := range c.registries {
		out[i] = c.registries[i]
	}
	return out
}

func (c *Controller) getRegistryIndex(clusterID cluster.ID, provider provider.ID) (int, bool) {
	for i, r := range c.registries {
		if r.Cluster().Equals(clusterID) && r.Provider() == provider {
			return i, true
		}
	}
	return 0, false
}

// Services lists services from all platforms
func (c *Controller) Services() ([]*model.Service, error) {
	// smap is a map of hostname (string) to service, used to identify services that
	// are installed in multiple clusters.
	smap := make(map[host.Name]*model.Service)

	services := make([]*model.Service, 0)
	var errs error
	// Locking Registries list while walking it to prevent inconsistent results
	for _, r := range c.GetRegistries() {
		svcs, err := r.Services()
		if err != nil {
			errs = multierror.Append(errs, err)
			continue
		}

		if r.Provider() != provider.Kubernetes {
			services = append(services, svcs...)
		} else {
			for _, s := range svcs {
				sp, ok := smap[s.Hostname]
				if !ok {
					// First time we see a service. The result will have a single service per hostname
					// The first cluster will be listed first, so the services in the primary cluster
					// will be used for default settings. If a service appears in multiple clusters,
					// the order is less clear.
					sp = s
					smap[s.Hostname] = sp
					services = append(services, sp)
				} else {
					// If it is seen second time, that means it is from a different cluster, update cluster VIPs.
					mergeService(sp, s, r)
				}
			}
		}
	}
	return services, errs
}

// GetService retrieves a service by hostname if exists
func (c *Controller) GetService(hostname host.Name) (*model.Service, error) {
	var errs error
	var out *model.Service
	for _, r := range c.GetRegistries() {
		service, err := r.GetService(hostname)
		if err != nil {
			errs = multierror.Append(errs, err)
			continue
		}
		if service == nil {
			continue
		}
		if r.Provider() != provider.Kubernetes {
			return service, nil
		}
		if out == nil {
			out = service.DeepCopy()
		} else {
			// If we are seeing the service for the second time, it means it is available in multiple clusters.
			mergeService(out, service, r)
		}
	}
	return out, errs
}

func mergeService(dst, src *model.Service, srcRegistry serviceregistry.Instance) {
	dst.Mutex.Lock()
	defer dst.Mutex.Unlock()
	if dst.ClusterVIPs == nil {
		dst.ClusterVIPs = make(map[cluster.ID]string)
	}
	// prefer the k8s VIP where possible
	if srcRegistry.Provider() == provider.Kubernetes || dst.ClusterVIPs[srcRegistry.Cluster()] == "" {
		dst.ClusterVIPs[srcRegistry.Cluster()] = src.Address
	}
}

// NetworkGateways merges the service-based cross-network gateways from each registry.
func (c *Controller) NetworkGateways() []*model.NetworkGateway {
	var gws []*model.NetworkGateway
	for _, r := range c.GetRegistries() {
		gws = append(gws, r.NetworkGateways()...)
	}
	return gws
}

// InstancesByPort retrieves instances for a service on a given port that match
// any of the supplied labels. All instances match an empty label list.
func (c *Controller) InstancesByPort(svc *model.Service, port int, labels labels.Collection) []*model.ServiceInstance {
	var instances []*model.ServiceInstance
	for _, r := range c.GetRegistries() {
		instances = append(instances, r.InstancesByPort(svc, port, labels)...)
	}
	return instances
}

func nodeClusterID(node *model.Proxy) cluster.ID {
	if node.Metadata == nil || node.Metadata.ClusterID == "" {
		return ""
	}
	return node.Metadata.ClusterID
}

// Skip the service registry when there won't be a match
// because the proxy is in a different cluster.
func skipSearchingRegistryForProxy(nodeClusterID cluster.ID, r serviceregistry.Instance) bool {
	// Always search non-kube (usually serviceentry) registry.
	// Check every registry if cluster ID isn't specified.
	if r.Provider() != provider.Kubernetes || nodeClusterID == "" {
		return false
	}

	return !r.Cluster().Equals(nodeClusterID)
}

// GetProxyServiceInstances lists service instances co-located with a given proxy
func (c *Controller) GetProxyServiceInstances(node *model.Proxy) []*model.ServiceInstance {
	out := make([]*model.ServiceInstance, 0)
	nodeClusterID := nodeClusterID(node)
	for _, r := range c.GetRegistries() {
		if skipSearchingRegistryForProxy(nodeClusterID, r) {
			log.Debugf("GetProxyServiceInstances(): not searching registry %v: proxy %v CLUSTER_ID is %v",
				r.Cluster(), node.ID, nodeClusterID)
			continue
		}

		instances := r.GetProxyServiceInstances(node)
		if len(instances) > 0 {
			out = append(out, instances...)
		}
	}

	return out
}

func (c *Controller) GetProxyWorkloadLabels(proxy *model.Proxy) labels.Collection {
	var out labels.Collection
	clusterID := nodeClusterID(proxy)
	for _, r := range c.GetRegistries() {
		// If proxy clusterID unset, we may find incorrect workload label.
		// This can not happen in k8s env.
		if clusterID == "" {
			wlLabels := r.GetProxyWorkloadLabels(proxy)
			if len(wlLabels) > 0 {
				out = append(out, wlLabels...)
				break
			}
		} else if clusterID == r.Cluster() {
			// find proxy in the specified cluster
			wlLabels := r.GetProxyWorkloadLabels(proxy)
			if len(wlLabels) > 0 {
				out = append(out, wlLabels...)
			}
			break
		}
	}

	return out
}

// Run starts all the controllers
func (c *Controller) Run(stop <-chan struct{}) {
	for _, r := range c.GetRegistries() {
		go r.Run(stop)
	}
	c.running.Store(true)
	<-stop
	log.Info("Registry Aggregator terminated")
}

// Running returns true after Run has been called. If already running, registries passed to AddRegistry
// should be started outside of this aggregate controller.
func (c *Controller) Running() bool {
	return c.running.Load()
}

// HasSynced returns true when all registries have synced
func (c *Controller) HasSynced() bool {
	for _, r := range c.GetRegistries() {
		if !r.HasSynced() {
			log.Debugf("registry %s is syncing", r.Cluster())
			return false
		}
	}
	return true
}

// AppendServiceHandler implements a service catalog operation
func (c *Controller) AppendServiceHandler(f func(*model.Service, model.Event)) {
	for _, r := range c.GetRegistries() {
		r.AppendServiceHandler(f)
	}
}

func (c *Controller) AppendWorkloadHandler(f func(*model.WorkloadInstance, model.Event)) {
	for _, r := range c.GetRegistries() {
		r.AppendWorkloadHandler(f)
	}
}

// GetIstioServiceAccounts implements model.ServiceAccounts operation.
// The returned list contains all SPIFFE based identities that backs the service.
// This method also expand the results from different registries based on the mesh config trust domain aliases.
// To retain such trust domain expansion behavior, the xDS server implementation should wrap any (even if single)
// service registry by this aggreated one.
// For example,
// - { "spiffe://cluster.local/bar@iam.gserviceaccount.com"}; when annotation is used on corresponding workloads.
// - { "spiffe://cluster.local/ns/default/sa/foo" }; normal kubernetes cases
// - { "spiffe://cluster.local/ns/default/sa/foo", "spiffe://trust-domain-alias/ns/default/sa/foo" };
//   if the trust domain alias is configured.
func (c *Controller) GetIstioServiceAccounts(svc *model.Service, ports []int) []string {
	out := map[string]struct{}{}
	for _, r := range c.GetRegistries() {
		svcAccounts := r.GetIstioServiceAccounts(svc, ports)
		for _, sa := range svcAccounts {
			out[sa] = struct{}{}
		}
	}
	result := make([]string, 0, len(out))
	for k := range out {
		result = append(result, k)
	}
	tds := []string{}
	if c.meshHolder != nil {
		mesh := c.meshHolder.Mesh()
		if mesh != nil {
			tds = mesh.TrustDomainAliases
		}
	}
	expanded := spiffe.ExpandWithTrustDomains(result, tds)
	result = make([]string, 0, len(expanded))
	for k := range expanded {
		result = append(result, k)
	}
	// Sort to make the return result deterministic.
	sort.Strings(result)
	return result
}
<commit_msg>remove clusterID empty check (#34813)<commit_after>// Copyright Istio 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 aggregate

import (
	"sort"
	"sync"

	"github.com/hashicorp/go-multierror"
	"go.uber.org/atomic"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/serviceregistry"
	"istio.io/istio/pilot/pkg/serviceregistry/provider"
	"istio.io/istio/pkg/cluster"
	"istio.io/istio/pkg/config/host"
	"istio.io/istio/pkg/config/labels"
	"istio.io/istio/pkg/config/mesh"
	"istio.io/istio/pkg/spiffe"
	"istio.io/pkg/log"
)

// The aggregate controller does not implement serviceregistry.Instance since it may be comprised of various
// providers and clusters.
var (
	_ model.ServiceDiscovery = &Controller{}
	_ model.Controller       = &Controller{}
)

// Controller aggregates data across different registries and monitors for changes
type Controller struct {
	registries []serviceregistry.Instance
	storeLock  sync.RWMutex
	meshHolder mesh.Holder
	running    *atomic.Bool
}

type Options struct {
	MeshHolder mesh.Holder
}

// NewController creates a new Aggregate controller
func NewController(opt Options) *Controller {
	return &Controller{
		registries: make([]serviceregistry.Instance, 0),
		meshHolder: opt.MeshHolder,
		running:    atomic.NewBool(false),
	}
}

// AddRegistry adds registries into the aggregated controller
func (c *Controller) AddRegistry(registry serviceregistry.Instance) {
	c.storeLock.Lock()
	defer c.storeLock.Unlock()

	c.registries = append(c.registries, registry)
}

// DeleteRegistry deletes specified registry from the aggregated controller
func (c *Controller) DeleteRegistry(clusterID cluster.ID, providerID provider.ID) {
	c.storeLock.Lock()
	defer c.storeLock.Unlock()

	if len(c.registries) == 0 {
		log.Warnf("Registry list is empty, nothing to delete")
		return
	}
	index, ok := c.getRegistryIndex(clusterID, providerID)
	if !ok {
		log.Warnf("Registry %s is not found in the registries list, nothing to delete", clusterID)
		return
	}
	c.registries = append(c.registries[:index], c.registries[index+1:]...)
	log.Infof("Registry for the cluster %s has been deleted.", clusterID)
}

// GetRegistries returns a copy of all registries
func (c *Controller) GetRegistries() []serviceregistry.Instance {
	c.storeLock.RLock()
	defer c.storeLock.RUnlock()

	// copy registries to prevent race, no need to deep copy here.
	out := make([]serviceregistry.Instance, len(c.registries))
	for i := range c.registries {
		out[i] = c.registries[i]
	}
	return out
}

func (c *Controller) getRegistryIndex(clusterID cluster.ID, provider provider.ID) (int, bool) {
	for i, r := range c.registries {
		if r.Cluster().Equals(clusterID) && r.Provider() == provider {
			return i, true
		}
	}
	return 0, false
}

// Services lists services from all platforms
func (c *Controller) Services() ([]*model.Service, error) {
	// smap is a map of hostname (string) to service, used to identify services that
	// are installed in multiple clusters.
	smap := make(map[host.Name]*model.Service)

	services := make([]*model.Service, 0)
	var errs error
	// Locking Registries list while walking it to prevent inconsistent results
	for _, r := range c.GetRegistries() {
		svcs, err := r.Services()
		if err != nil {
			errs = multierror.Append(errs, err)
			continue
		}

		if r.Provider() != provider.Kubernetes {
			services = append(services, svcs...)
		} else {
			for _, s := range svcs {
				sp, ok := smap[s.Hostname]
				if !ok {
					// First time we see a service. The result will have a single service per hostname
					// The first cluster will be listed first, so the services in the primary cluster
					// will be used for default settings. If a service appears in multiple clusters,
					// the order is less clear.
					sp = s
					smap[s.Hostname] = sp
					services = append(services, sp)
				} else {
					// If it is seen second time, that means it is from a different cluster, update cluster VIPs.
					mergeService(sp, s, r)
				}
			}
		}
	}
	return services, errs
}

// GetService retrieves a service by hostname if exists
func (c *Controller) GetService(hostname host.Name) (*model.Service, error) {
	var errs error
	var out *model.Service
	for _, r := range c.GetRegistries() {
		service, err := r.GetService(hostname)
		if err != nil {
			errs = multierror.Append(errs, err)
			continue
		}
		if service == nil {
			continue
		}
		if r.Provider() != provider.Kubernetes {
			return service, nil
		}
		if out == nil {
			out = service.DeepCopy()
		} else {
			// If we are seeing the service for the second time, it means it is available in multiple clusters.
			mergeService(out, service, r)
		}
	}
	return out, errs
}

func mergeService(dst, src *model.Service, srcRegistry serviceregistry.Instance) {
	dst.Mutex.Lock()
	defer dst.Mutex.Unlock()
	if dst.ClusterVIPs == nil {
		dst.ClusterVIPs = make(map[cluster.ID]string)
	}
	// prefer the k8s VIP where possible
	if srcRegistry.Provider() == provider.Kubernetes {
		dst.ClusterVIPs[srcRegistry.Cluster()] = src.Address
	}
}

// NetworkGateways merges the service-based cross-network gateways from each registry.
func (c *Controller) NetworkGateways() []*model.NetworkGateway {
	var gws []*model.NetworkGateway
	for _, r := range c.GetRegistries() {
		gws = append(gws, r.NetworkGateways()...)
	}
	return gws
}

// InstancesByPort retrieves instances for a service on a given port that match
// any of the supplied labels. All instances match an empty label list.
func (c *Controller) InstancesByPort(svc *model.Service, port int, labels labels.Collection) []*model.ServiceInstance {
	var instances []*model.ServiceInstance
	for _, r := range c.GetRegistries() {
		instances = append(instances, r.InstancesByPort(svc, port, labels)...)
	}
	return instances
}

func nodeClusterID(node *model.Proxy) cluster.ID {
	if node.Metadata == nil || node.Metadata.ClusterID == "" {
		return ""
	}
	return node.Metadata.ClusterID
}

// Skip the service registry when there won't be a match
// because the proxy is in a different cluster.
func skipSearchingRegistryForProxy(nodeClusterID cluster.ID, r serviceregistry.Instance) bool {
	// Always search non-kube (usually serviceentry) registry.
	// Check every registry if cluster ID isn't specified.
	if r.Provider() != provider.Kubernetes || nodeClusterID == "" {
		return false
	}

	return !r.Cluster().Equals(nodeClusterID)
}

// GetProxyServiceInstances lists service instances co-located with a given proxy
func (c *Controller) GetProxyServiceInstances(node *model.Proxy) []*model.ServiceInstance {
	out := make([]*model.ServiceInstance, 0)
	nodeClusterID := nodeClusterID(node)
	for _, r := range c.GetRegistries() {
		if skipSearchingRegistryForProxy(nodeClusterID, r) {
			log.Debugf("GetProxyServiceInstances(): not searching registry %v: proxy %v CLUSTER_ID is %v",
				r.Cluster(), node.ID, nodeClusterID)
			continue
		}

		instances := r.GetProxyServiceInstances(node)
		if len(instances) > 0 {
			out = append(out, instances...)
		}
	}

	return out
}

func (c *Controller) GetProxyWorkloadLabels(proxy *model.Proxy) labels.Collection {
	var out labels.Collection
	clusterID := nodeClusterID(proxy)
	for _, r := range c.GetRegistries() {
		// If proxy clusterID unset, we may find incorrect workload label.
		// This can not happen in k8s env.
		if clusterID == "" {
			wlLabels := r.GetProxyWorkloadLabels(proxy)
			if len(wlLabels) > 0 {
				out = append(out, wlLabels...)
				break
			}
		} else if clusterID == r.Cluster() {
			// find proxy in the specified cluster
			wlLabels := r.GetProxyWorkloadLabels(proxy)
			if len(wlLabels) > 0 {
				out = append(out, wlLabels...)
			}
			break
		}
	}

	return out
}

// Run starts all the controllers
func (c *Controller) Run(stop <-chan struct{}) {
	for _, r := range c.GetRegistries() {
		go r.Run(stop)
	}
	c.running.Store(true)
	<-stop
	log.Info("Registry Aggregator terminated")
}

// Running returns true after Run has been called. If already running, registries passed to AddRegistry
// should be started outside of this aggregate controller.
func (c *Controller) Running() bool {
	return c.running.Load()
}

// HasSynced returns true when all registries have synced
func (c *Controller) HasSynced() bool {
	for _, r := range c.GetRegistries() {
		if !r.HasSynced() {
			log.Debugf("registry %s is syncing", r.Cluster())
			return false
		}
	}
	return true
}

// AppendServiceHandler implements a service catalog operation
func (c *Controller) AppendServiceHandler(f func(*model.Service, model.Event)) {
	for _, r := range c.GetRegistries() {
		r.AppendServiceHandler(f)
	}
}

func (c *Controller) AppendWorkloadHandler(f func(*model.WorkloadInstance, model.Event)) {
	for _, r := range c.GetRegistries() {
		r.AppendWorkloadHandler(f)
	}
}

// GetIstioServiceAccounts implements model.ServiceAccounts operation.
// The returned list contains all SPIFFE based identities that backs the service.
// This method also expand the results from different registries based on the mesh config trust domain aliases.
// To retain such trust domain expansion behavior, the xDS server implementation should wrap any (even if single)
// service registry by this aggreated one.
// For example,
// - { "spiffe://cluster.local/bar@iam.gserviceaccount.com"}; when annotation is used on corresponding workloads.
// - { "spiffe://cluster.local/ns/default/sa/foo" }; normal kubernetes cases
// - { "spiffe://cluster.local/ns/default/sa/foo", "spiffe://trust-domain-alias/ns/default/sa/foo" };
//   if the trust domain alias is configured.
func (c *Controller) GetIstioServiceAccounts(svc *model.Service, ports []int) []string {
	out := map[string]struct{}{}
	for _, r := range c.GetRegistries() {
		svcAccounts := r.GetIstioServiceAccounts(svc, ports)
		for _, sa := range svcAccounts {
			out[sa] = struct{}{}
		}
	}
	result := make([]string, 0, len(out))
	for k := range out {
		result = append(result, k)
	}
	tds := []string{}
	if c.meshHolder != nil {
		mesh := c.meshHolder.Mesh()
		if mesh != nil {
			tds = mesh.TrustDomainAliases
		}
	}
	expanded := spiffe.ExpandWithTrustDomains(result, tds)
	result = make([]string, 0, len(expanded))
	for k := range expanded {
		result = append(result, k)
	}
	// Sort to make the return result deterministic.
	sort.Strings(result)
	return result
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"

	git "github.com/libgit2/git2go"
	"github.com/sirupsen/logrus"
)

type BuildStage = int

var DefaultCheckoutOptions = &git.CheckoutOpts{
	Strategy: git.CheckoutSafe,
}

const (
	BUILD_STAGE_BEFORE_BUILD = iota
	BUILD_STAGE_AFTER_BUILD
	BUILD_STAGE_SKIP_BUILD
)

func Build(m *Manifest, stdin io.Reader, stdout, stderr io.Writer, buildStageCallback func(app *Application, s BuildStage)) error {
	repo, err := git.OpenRepository(m.Dir)
	if err != nil {
		return err
	}

	oid, err := git.NewOid(m.Sha)
	if err != nil {
		return err
	}

	commit, err := repo.LookupCommit(oid)
	if err != nil {
		return err
	}

	tree, err := commit.Tree()
	if err != nil {
		return err
	}

	// TODO: Confirm the strategy is correct
	err = repo.CheckoutTree(tree, DefaultCheckoutOptions)
	if err != nil {
		return err
	}

	defer checkoutHead(repo)

	for _, a := range m.Applications {
		if !canBuildHere(a) {
			buildStageCallback(a, BUILD_STAGE_SKIP_BUILD)
			continue
		}

		buildStageCallback(a, BUILD_STAGE_BEFORE_BUILD)
		err := buildOne(m.Dir, a, stdin, stdout, stderr)
		if err != nil {
			return err
		}
		buildStageCallback(a, BUILD_STAGE_AFTER_BUILD)
	}

	return nil
}

func setupAppBuildEnvironment(app *Application) []string {
	r := []string{
		fmt.Sprintf("MBT_APP_VERSION=%s", app.Version),
	}

	for k, v := range app.Properties {
		if value, ok := v.(string); ok {
			r = append(r, fmt.Sprintf("MBT_APP_PROPERTY_%s=%s", strings.ToUpper(k), value))
		}
	}

	return r
}

func buildOne(dir string, app *Application, stdin io.Reader, stdout, stderr io.Writer) error {
	build := app.Build[runtime.GOOS]
	cmd := exec.Command(build.Cmd)
	cmd.Env = append(os.Environ(), setupAppBuildEnvironment(app)...)
	cmd.Dir = path.Join(dir, app.Path)
	cmd.Stdin = stdin
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	cmd.Args = append(cmd.Args, build.Args...)
	err := cmd.Run()
	return err
}

func canBuildHere(app *Application) bool {
	_, ok := app.Build[runtime.GOOS]
	return ok
}

func checkoutHead(repo *git.Repository) {
	err := repo.CheckoutHead(DefaultCheckoutOptions)
	if err != nil {
		logrus.Warnf("failed to checkout head: %s", err)
	}
}
<commit_msg>use force strategy when checking back out the head. - this should be harmless as we wouldn't have been able to switch to target branch if the the working directory was dirty.<commit_after>package lib

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"

	git "github.com/libgit2/git2go"
	"github.com/sirupsen/logrus"
)

type BuildStage = int

var DefaultCheckoutOptions = &git.CheckoutOpts{
	Strategy: git.CheckoutSafe,
}

const (
	BUILD_STAGE_BEFORE_BUILD = iota
	BUILD_STAGE_AFTER_BUILD
	BUILD_STAGE_SKIP_BUILD
)

func Build(m *Manifest, stdin io.Reader, stdout, stderr io.Writer, buildStageCallback func(app *Application, s BuildStage)) error {
	repo, err := git.OpenRepository(m.Dir)
	if err != nil {
		return err
	}

	oid, err := git.NewOid(m.Sha)
	if err != nil {
		return err
	}

	commit, err := repo.LookupCommit(oid)
	if err != nil {
		return err
	}

	tree, err := commit.Tree()
	if err != nil {
		return err
	}

	// TODO: Confirm the strategy is correct
	err = repo.CheckoutTree(tree, DefaultCheckoutOptions)
	if err != nil {
		return err
	}

	defer checkoutHead(repo)

	for _, a := range m.Applications {
		if !canBuildHere(a) {
			buildStageCallback(a, BUILD_STAGE_SKIP_BUILD)
			continue
		}

		buildStageCallback(a, BUILD_STAGE_BEFORE_BUILD)
		err := buildOne(m.Dir, a, stdin, stdout, stderr)
		if err != nil {
			return err
		}
		buildStageCallback(a, BUILD_STAGE_AFTER_BUILD)
	}

	return nil
}

func setupAppBuildEnvironment(app *Application) []string {
	r := []string{
		fmt.Sprintf("MBT_APP_VERSION=%s", app.Version),
	}

	for k, v := range app.Properties {
		if value, ok := v.(string); ok {
			r = append(r, fmt.Sprintf("MBT_APP_PROPERTY_%s=%s", strings.ToUpper(k), value))
		}
	}

	return r
}

func buildOne(dir string, app *Application, stdin io.Reader, stdout, stderr io.Writer) error {
	build := app.Build[runtime.GOOS]
	cmd := exec.Command(build.Cmd)
	cmd.Env = append(os.Environ(), setupAppBuildEnvironment(app)...)
	cmd.Dir = path.Join(dir, app.Path)
	cmd.Stdin = stdin
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	cmd.Args = append(cmd.Args, build.Args...)
	err := cmd.Run()
	return err
}

func canBuildHere(app *Application) bool {
	_, ok := app.Build[runtime.GOOS]
	return ok
}

func checkoutHead(repo *git.Repository) {
	err := repo.CheckoutHead(&git.CheckoutOpts{Strategy: git.CheckoutForce})
	if err != nil {
		logrus.Warnf("failed to checkout head: %s", err)
	}
}
<|endoftext|>


<commit_before>package qbit

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestConstraints(t *testing.T) {

	assert.Equal(t, NotNull(), Constraint{"NOT NULL"})
	assert.Equal(t, Default(5), Constraint{"DEFAULT `5`"})
	assert.Equal(t, Default("-"), Constraint{"DEFAULT `-`"})
}
<commit_msg>add new tests for new constraints<commit_after>package qbit

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestConstraints(t *testing.T) {

	assert.Equal(t, NotNull(), Constraint{"NOT NULL"})
	assert.Equal(t, Default(5), Constraint{"DEFAULT `5`"})
	assert.Equal(t, Default("-"), Constraint{"DEFAULT `-`"})
	assert.Equal(t, Unique(), Constraint{"UNIQUE"})
	assert.Equal(t, Unique("email", "name"), Constraint{"UNIQUE(email, name)"})
	assert.Equal(t, Primary(), Constraint{"PRIMARY KEY"})
	assert.Equal(t, Primary("email", "password"), Constraint{"PRIMARY KEY(email, password)"})
	assert.Equal(t, Foreign("user_id", "profile", "user_id"), Constraint{"FOREIGN KEY (user_id) REFERENCES profile(user_id)"})
}
<|endoftext|>