<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	openrtb "gopkg.in/bsm/openrtb.v2"
)

//OutputPerSeconds = number of seconds between stat output
var OutputPerSeconds = 10

//Wins = counter for # of wins in the last OutputPerSeconds
var Wins = 0

//Events = counter for # of events in the last OutputPerSeconds
var Events = 0

//Bids = counter for # of bids in the last OutputPerSeconds
var Bids = 0

var outputChannel = make(chan bool)

type Creative struct {
	Format string `json:"format"`
	ID     int    `json:"id"`
	Name   string `json:"name"`
}

// This is the agent configuration that will be sent to RTBKIT's ACS
type AgentConfig struct {
	// We use `RawMessage` for Augmentations and BidcControl, because we
	// don't need it, we just cache it.
	Account            []string         `json:"account"`
	Augmentations      *json.RawMessage `json:"augmentations"`
	BidControl         *json.RawMessage `json:"bidControl"`
	BidProbability     float64          `json:"bidProbability"`
	Creatives          []Creative       `json:"creatives"`
	ErrorFormat        string           `json:"errorFormat"`
	External           bool             `json:"external"`
	ExternalId         int              `json:"externalId"`
	LossFormat         string           `json:"lossFormat"`
	MinTimeAvailableMs float64          `json:"minTimeAvailableMs"`
	WinFormat          string           `json:"winFormat"`
	BidderInterface    string           `json:"bidderInterface"`
}

// This represents a RTBKIT Agent
type Agent struct {
	Name   string      `json:"name"`
	Config AgentConfig `json:"config"`

	// This is the price the agent will pay per impression. "Fixed price bidder".
	Price float64 `json:"price"`

	// For pacing the budgeting
	Period  int `json:"period"`
	Balance int `json:"balance"`

	// private state of each agent
	registered bool      // did we register the configuration in the ACS?
	pacer      chan bool // go routine updating balance in the banker
	bidID      int       // unique id for response
}

type creativesKey struct {
	// This is used to make a mapping between an impression and the
	// external-id of an agent to the creatives that can be sent to the
	// exchange for that impression.
	ImpID string
	ExtID int
}

// RegisterAgent in the ACS sending a HTTP request to the service on `acsIp`:`acsPort`
func (agent *Agent) RegisterAgent(
	httpClient *http.Client, acsIp string, acsPort int) {
	url := fmt.Sprintf("http://%s:%d/v1/agents/%s/config", acsIp, acsPort, agent.Name)
	body, _ := json.Marshal(agent.Config)
	reader := bytes.NewReader(body)
	req, _ := http.NewRequest("POST", url, reader)
	req.Header.Add("Accept", "application/json")
	res, err := httpClient.Do(req)
	if err != nil {
		log.Printf("ACS registration failed with %s\n", err)
		return
	}
	agent.registered = true
	res.Body.Close()
}

// UnregisterAgent Removes the agent configuration from the ACS
func (agent *Agent) UnregisterAgent(
	httpClient *http.Client, acsIp string, acsPort int) {
	url := fmt.Sprintf("http://%s:%d/v1/agents/%s/config", acsIp, acsPort, agent.Name)
	req, _ := http.NewRequest("DELETE", url, bytes.NewBufferString(""))
	res, err := httpClient.Do(req)
	if err != nil {
		log.Printf("Unregister failed with %s\n", err)
		return
	}
	agent.registered = false
	res.Body.Close()
}

// Starts a go routine which periodically updates the balance on the agents account.
func (agent *Agent) StartPacer(
	httpClient *http.Client, bankerIp string, bankerPort int) {

	accounts := agent.Config.Account

	url := fmt.Sprintf("http://%s:%d/v1/accounts/%s/balance",
		bankerIp, bankerPort, strings.Join(accounts, ":"))
	body := fmt.Sprintf("{\"USD/1M\": %d}", agent.Balance)
	ticker := time.NewTicker(time.Duration(agent.Period) * time.Millisecond)
	agent.pacer = make(chan bool)

	go func() {
		for {
			select {
			case <-ticker.C:
				// make this a new go routine?
				go func() {
					log.Println("Pacing...")
					req, _ := http.NewRequest("POST", url, strings.NewReader(body))
					req.Header.Add("Accept", "application/json")
					res, err := httpClient.Do(req)
					if err != nil {
						log.Printf("Balance failed with %s\n", err)
						return
					}
					res.Body.Close()
				}()
			case <-agent.pacer:
				ticker.Stop()
				return
			}
		}
	}()
}

// StartStatOutput is responsible for displaying the number of wins and events per timer tick
func StartStatOutput() {
	tickerChannel := time.NewTicker(time.Second * time.Duration(OutputPerSeconds)).C

	go func() {

		for {
			select {
			case <-tickerChannel:
				printStats()
			case <-outputChannel:
				return
			}
		}
	}()
}

func BidWin() {
	Wins++
}

func BidEvent() {
	Events++
}

func BidIncoming() {
	Bids++
}

func printStats() {
	tempWins := Wins
	Wins = 0
	winsPerSecond := tempWins / OutputPerSeconds
	tempEvents := Events
	Events = 0
	eventsPerSecond := tempEvents / OutputPerSeconds
	tempBids := Bids
	Bids = 0
	bidsPerSecond := tempBids / OutputPerSeconds
	log.Println("***********************")
	log.Printf("Bids: %d (%d/second)", tempBids, bidsPerSecond)
	log.Printf("Wins: %d (%d/second)", tempWins, winsPerSecond)
	log.Printf("Events: %d (%d/second)", tempEvents, eventsPerSecond)
}

// Stops the go routine updating the bank balance.
func (agent *Agent) StopPacer() {
	close(outputChannel)
	close(agent.pacer)
}

// Adds to the bid response the bid by the agent. The Bid is added to
// the only seat of the response. It picks a random creative from
// the list of creatives from the `Agent.Config.Creative` and places it
// in the bid.
func (agent *Agent) DoBid(
	req *openrtb.BidRequest, res *openrtb.BidResponse, ids map[creativesKey]interface{}) (*openrtb.BidResponse, bool) {

	for _, imp := range req.Imp {
		key := creativesKey{ImpID: imp.ID, ExtID: agent.Config.ExternalId}
		if ids[key] == nil {
			continue
		}

		creativeList := ids[key].([]interface{})
		// pick a random creative
		n := rand.Intn(len(creativeList))

		// Extract a usable creative ID from the JSON parse
		crid := strconv.Itoa(int(creativeList[n].(float64)))

		bidID := strconv.Itoa(agent.bidID)

		price := float64(agent.Price)

		ext := map[string]interface{}{"priority": 1.0, "external-id": agent.Config.ExternalId}
		jsonExt, _ := json.Marshal(ext)
		bid := openrtb.Bid{ID: bidID, ImpID: imp.ID, CreativeID: crid, Price: price * rand.Float64(), Ext: jsonExt}
		agent.bidID++
		res.SeatBid[0].Bid = append(res.SeatBid[0].Bid, bid)
	}
	return res, len(res.SeatBid[0].Bid) > 0
}

func externalIdsFromRequest(req *openrtb.BidRequest) map[creativesKey]interface{} {
	// This function makes a mappping with a range of type (Impression Id, External Id)
	// to a slice of "creative indexes" (See the agent configuration "creative").
	// We use this auxiliary function in `DoBid` to match the `BidRequest` to the
	// creatives of the agent and create a response.
	ids := make(map[creativesKey]interface{})

	for _, imp := range req.Imp {
		var extJSON map[string]interface{}
		_ = json.Unmarshal(imp.Ext, &extJSON)

		for _, extID := range extJSON["external-ids"].([]interface{}) {
			extID = int(extID.(float64))
			key := creativesKey{ImpID: imp.ID, ExtID: extID.(int)}
			creatives := (extJSON["creative-ids"].(map[string]interface{}))[strconv.Itoa(extID.(int))]
			ids[key] = creatives.(interface{})
		}
	}
	return ids
}

func emptyResponseWithOneSeat(req *openrtb.BidRequest) *openrtb.BidResponse {
	// This function adds a Seat to the Response.
	// Seat: A buyer entity that uses a Bidder to obtain impressions on its behalf.
	seat := openrtb.SeatBid{Bid: make([]openrtb.Bid, 0)}
	seatbid := []openrtb.SeatBid{seat}
	res := &openrtb.BidResponse{ID: req.ID, SeatBid: seatbid}
	return res
}

// Parse a JSON file and return an Agent.
func LoadAgent(filepath string) (Agent, error) {
	var agent Agent
	data, err := ioutil.ReadFile(filepath)
	if err != nil {
		return Agent{}, err
	}
	err = json.Unmarshal(data, &agent)
	if err != nil {
		return Agent{}, err
	}
	return agent, nil
}

// Parse a JSON file and return a list of Agents.
func LoadAgentsFromFile(filepath string) ([]Agent, error) {
	type Agents []Agent
	var agents Agents

	data, err := ioutil.ReadFile(filepath)
	if err != nil {
		return agents, err
	}
	err = json.Unmarshal(data, &agents)
	if err != nil {
		return agents, err
	}
	return agents, nil
}

//FindCreativeIndexFromID takes a creative ID and an AgentConfig,
// returning a creative index usable by the RTBKit router
func FindCreativeIndexFromID(crid int, agent AgentConfig) (string, error) {
	for creativeIndex, creative := range agent.Creatives {
		if creative.ID == crid {
			return strconv.Itoa(creativeIndex), nil
		}
	}
	return "", errors.New("Unable to find matching creative")
}
<commit_msg>cleaning up the agent a bit<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	openrtb "gopkg.in/bsm/openrtb.v2"
)

//OutputPerSeconds = number of seconds between stat output
var OutputPerSeconds = 10

//Wins = counter for # of wins in the last OutputPerSeconds
var Wins = 0

//Events = counter for # of events in the last OutputPerSeconds
var Events = 0

//Bids = counter for # of bids in the last OutputPerSeconds
var Bids = 0

var outputChannel = make(chan bool)

// Creative ...
type Creative struct {
	Format string `json:"format"`
	ID     int    `json:"id"`
	Name   string `json:"name"`
}

// AgentConfig This is the agent configuration that will be sent to RTBKIT's ACS
type AgentConfig struct {
	// We use `RawMessage` for Augmentations and BidcControl, because we
	// don't need it, we just cache it.
	Account            []string         `json:"account"`
	Augmentations      *json.RawMessage `json:"augmentations"`
	BidControl         *json.RawMessage `json:"bidControl"`
	BidProbability     float64          `json:"bidProbability"`
	Creatives          []Creative       `json:"creatives"`
	ErrorFormat        string           `json:"errorFormat"`
	External           bool             `json:"external"`
	ExternalId         int              `json:"externalId"`
	LossFormat         string           `json:"lossFormat"`
	MinTimeAvailableMs float64          `json:"minTimeAvailableMs"`
	WinFormat          string           `json:"winFormat"`
	BidderInterface    string           `json:"bidderInterface"`
}

// Agent This represents a RTBKIT Agent
type Agent struct {
	Name   string      `json:"name"`
	Config AgentConfig `json:"config"`

	// This is the price the agent will pay per impression. "Fixed price bidder".
	Price float64 `json:"price"`

	// For pacing the budgeting
	Period  int `json:"period"`
	Balance int `json:"balance"`

	// private state of each agent
	registered bool      // did we register the configuration in the ACS?
	pacer      chan bool // go routine updating balance in the banker
	bidID      int       // unique id for response
}

type creativesKey struct {
	// This is used to make a mapping between an impression and the
	// external-id of an agent to the creatives that can be sent to the
	// exchange for that impression.
	ImpID string
	ExtID int
}

// RegisterAgent in the ACS sending a HTTP request to the service on `acsIp`:`acsPort`
func (agent *Agent) RegisterAgent(httpClient *http.Client, acsIP string, acsPort int) {
	url := fmt.Sprintf("http://%s:%d/v1/agents/%s/config", acsIP, acsPort, agent.Name)
	body, _ := json.Marshal(agent.Config)
	reader := bytes.NewReader(body)
	req, _ := http.NewRequest("POST", url, reader)
	req.Header.Add("Accept", "application/json")
	res, err := httpClient.Do(req)
	if err != nil {
		log.Printf("ACS registration failed with %s\n", err)
		return
	}
	agent.registered = true
	res.Body.Close()
}

// UnregisterAgent Removes the agent configuration from the ACS
func (agent *Agent) UnregisterAgent(
	httpClient *http.Client, acsIp string, acsPort int) {
	url := fmt.Sprintf("http://%s:%d/v1/agents/%s/config", acsIp, acsPort, agent.Name)
	req, _ := http.NewRequest("DELETE", url, bytes.NewBufferString(""))
	res, err := httpClient.Do(req)
	if err != nil {
		log.Printf("Unregister failed with %s\n", err)
		return
	}
	agent.registered = false
	res.Body.Close()
}

// StartPacer Starts a go routine which periodically updates the balance on the agents account.
func (agent *Agent) StartPacer(
	httpClient *http.Client, bankerIp string, bankerPort int) {

	accounts := agent.Config.Account

	url := fmt.Sprintf("http://%s:%d/v1/accounts/%s/balance",
		bankerIp, bankerPort, strings.Join(accounts, ":"))
	body := fmt.Sprintf("{\"USD/1M\": %d}", agent.Balance)
	ticker := time.NewTicker(time.Duration(agent.Period) * time.Millisecond)
	agent.pacer = make(chan bool)

	go func() {
		for {
			select {
			case <-ticker.C:
				// make this a new go routine?
				go func() {
					log.Println("Pacing...")
					req, _ := http.NewRequest("POST", url, strings.NewReader(body))
					req.Header.Add("Accept", "application/json")
					res, err := httpClient.Do(req)
					if err != nil {
						log.Printf("Balance failed with %s\n", err)
						return
					}
					res.Body.Close()
				}()
			case <-agent.pacer:
				ticker.Stop()
				return
			}
		}
	}()
}

// StartStatOutput is responsible for displaying the number of wins and events per timer tick
func StartStatOutput() {
	tickerChannel := time.NewTicker(time.Second * time.Duration(OutputPerSeconds)).C

	go func() {

		for {
			select {
			case <-tickerChannel:
				printStats()
			case <-outputChannel:
				return
			}
		}
	}()
}

// BidWin ...
func BidWin() {
	Wins++
}

// BidEvent ...
func BidEvent() {
	Events++
}

// BidIncoming ...
func BidIncoming() {
	Bids++
}

func printStats() {
	tempWins := Wins
	Wins = 0
	winsPerSecond := tempWins / OutputPerSeconds
	tempEvents := Events
	Events = 0
	eventsPerSecond := tempEvents / OutputPerSeconds
	tempBids := Bids
	Bids = 0
	bidsPerSecond := tempBids / OutputPerSeconds
	log.Println("***********************")
	log.Printf("Bids: %d (%d/second)", tempBids, bidsPerSecond)
	log.Printf("Wins: %d (%d/second)", tempWins, winsPerSecond)
	log.Printf("Events: %d (%d/second)", tempEvents, eventsPerSecond)
}

// StopPacer Stops the go routine updating the bank balance.
func (agent *Agent) StopPacer() {
	close(outputChannel)
	close(agent.pacer)
}

// DoBid Adds to the bid response the bid by the agent. The Bid is added to
// the only seat of the response. It picks a random creative from
// the list of creatives from the `Agent.Config.Creative` and places it
// in the bid.
func (agent *Agent) DoBid(
	req *openrtb.BidRequest, res *openrtb.BidResponse, ids map[creativesKey]interface{}) (*openrtb.BidResponse, bool) {

	for _, imp := range req.Imp {
		key := creativesKey{ImpID: imp.ID, ExtID: agent.Config.ExternalId}
		if ids[key] == nil {
			continue
		}

		creativeList := ids[key].([]interface{})
		// pick a random creative
		n := rand.Intn(len(creativeList))

		// Extract a usable creative ID from the JSON parse
		crid := strconv.Itoa(int(creativeList[n].(float64)))

		bidID := strconv.Itoa(agent.bidID)

		price := float64(agent.Price)

		ext := map[string]interface{}{"priority": 1.0, "external-id": agent.Config.ExternalId}
		jsonExt, _ := json.Marshal(ext)
		bid := openrtb.Bid{ID: bidID, ImpID: imp.ID, CreativeID: crid, Price: price * rand.Float64(), Ext: jsonExt}
		agent.bidID++
		res.SeatBid[0].Bid = append(res.SeatBid[0].Bid, bid)
	}
	return res, len(res.SeatBid[0].Bid) > 0
}

func externalIdsFromRequest(req *openrtb.BidRequest) map[creativesKey]interface{} {
	// This function makes a mappping with a range of type (Impression Id, External Id)
	// to a slice of "creative indexes" (See the agent configuration "creative").
	// We use this auxiliary function in `DoBid` to match the `BidRequest` to the
	// creatives of the agent and create a response.
	ids := make(map[creativesKey]interface{})

	for _, imp := range req.Imp {
		var extJSON map[string]interface{}
		_ = json.Unmarshal(imp.Ext, &extJSON)

		for _, extID := range extJSON["external-ids"].([]interface{}) {
			extID = int(extID.(float64))
			key := creativesKey{ImpID: imp.ID, ExtID: extID.(int)}
			creatives := (extJSON["creative-ids"].(map[string]interface{}))[strconv.Itoa(extID.(int))]
			ids[key] = creatives.(interface{})
		}
	}
	return ids
}

func emptyResponseWithOneSeat(req *openrtb.BidRequest) *openrtb.BidResponse {
	// This function adds a Seat to the Response.
	// Seat: A buyer entity that uses a Bidder to obtain impressions on its behalf.
	seat := openrtb.SeatBid{Bid: make([]openrtb.Bid, 0)}
	seatbid := []openrtb.SeatBid{seat}
	res := &openrtb.BidResponse{ID: req.ID, SeatBid: seatbid}
	return res
}

// LoadAgent Parse a JSON file and return an Agent.
func LoadAgent(filepath string) (Agent, error) {
	var agent Agent
	data, err := ioutil.ReadFile(filepath)
	if err != nil {
		return Agent{}, err
	}
	err = json.Unmarshal(data, &agent)
	if err != nil {
		return Agent{}, err
	}
	return agent, nil
}

// LoadAgentsFromFile Parse a JSON file and return a list of Agents.
func LoadAgentsFromFile(filepath string) ([]Agent, error) {
	type Agents []Agent
	var agents Agents

	data, err := ioutil.ReadFile(filepath)
	if err != nil {
		return agents, err
	}
	err = json.Unmarshal(data, &agents)
	if err != nil {
		return agents, err
	}
	return agents, nil
}

// FindCreativeIndexFromID takes a creative ID and an AgentConfig,
// returning a creative index usable by the RTBKit router
func FindCreativeIndexFromID(crid int, agent AgentConfig) (string, error) {
	for creativeIndex, creative := range agent.Creatives {
		if creative.ID == crid {
			return strconv.Itoa(creativeIndex), nil
		}
	}
	return "", errors.New("Unable to find matching creative")
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018 Johannes Kohnen <jwkohnen-github@ko-sys.com>
 *
 * 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 airac

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	format        = "2006-01-02"
	cycleDuration = 24192e11 // 4 weeks
)

var (
	epoch = time.Date(1901, time.January, 10, 0, 0, 0, 0, time.UTC)
)

type (
	// AIRAC represents an Aeronautical Information Regulation And Control
	// (AIRAC) cycle.
	AIRAC uint16

	// Airac is a deprecated alias to AIRAC.
	Airac = AIRAC
)

// Effective returns the effective date of this AIRAC cycle.
func (a AIRAC) Effective() time.Time {
	return epoch.Add(time.Duration(a) * cycleDuration)
}

// Year returns the year for this AIRAC cycle's identifier.
func (a AIRAC) Year() int {
	return a.Effective().Year()
}

// Ordinal returns the ordinal for this AIRAC cycle's identifier.
func (a AIRAC) Ordinal() int {
	return (a.Effective().YearDay()-1)/28 + 1
}

// FromDate returns the AIRAC cycle that occurred at date. A date before the
// internal epoch (1901-01-10) may return wrong data. The upper limit is year
// 2192.
func FromDate(date time.Time) AIRAC {
	a := date.Sub(epoch) / cycleDuration
	return AIRAC(a)
}

// FromString returns an AIRAC cycle that matches the identifier <yyoo>, i.e.
// the last two digits of the year and the ordinal, each with leading zeros.
// This works for years between 1964 and 2063. Identifiers between "6401" and
// "9913" are interpreted as AIRAC cycles between the years 1964 and 1999
// inclusive. AIRAC cycles between "0001" and "6313" are interpreted as AIRAC
// cycles between the years 2000 and 2063 inclusive.
func FromString(yyoo string) (AIRAC, error) {
	year, ordinal, err := parseIdentifier(yyoo)
	if err != nil {
		return 0, err
	}

	lastAiracOfPreviousYear := FromDate(time.Date(year-1, time.December, 31, 0, 0, 0, 0, time.UTC))
	airac := lastAiracOfPreviousYear + AIRAC(ordinal)

	if airac.Year() != year {
		return 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	return airac, nil
}

func parseIdentifier(yyoo string) (year, ordinal int, err error) {
	yyoo = strings.TrimSpace(yyoo)
	if len(yyoo) != 4 {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}
	yyooInt, err := strconv.Atoi(yyoo)
	if err != nil {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	year, ordinal = (yyooInt/100)+1900, yyooInt%100
	if year <= 1963 {
		year += 100
	}
	return year, ordinal, nil
}

// FromStringMust returns an AIRAC cycle that matches the identifier <yyoo>
// like FromString, but does not return an error. If there is an error it will
// panic instead.
func FromStringMust(yyoo string) AIRAC {
	airac, err := FromString(yyoo)
	if err != nil {
		panic(err)
	}
	return airac
}

// String returns a short representation of this AIRAC cycle. "YYOO"
func (a AIRAC) String() string {
	return fmt.Sprintf("%02d%02d", a.Year()%100, a.Ordinal())
}

// LongString returns a verbose representation of this AIRAC cycle.
// "YYOO (effective: YYYY-MM-DD; expires: YYYY-MM-DD)"
func (a AIRAC) LongString() string {
	n := a + 1
	return fmt.Sprintf("%02d%02d (effective: %s; expires: %s)",
		a.Year()%100,
		a.Ordinal(),
		a.Effective().Format(format),
		n.Effective().Add(-1).Format(format),
	)
}

// ByChrono is an []AIRAC wrapper, that satisfies sort.Interface and can be
// used to chronologically sort AIRAC instances.
type ByChrono []AIRAC

// Len ist the number of elements in the collection.
func (c ByChrono) Len() int { return len(c) }

// Less reports whether the element with index i should sort before the element
// with index j.
func (c ByChrono) Less(i, j int) bool { return c[i] < c[j] }

// Swap swaps the elements with indexes i and j.
func (c ByChrono) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

// static assert
var _ sort.Interface = ByChrono{}
<commit_msg>Use zero value for static type assert<commit_after>/*
 * Copyright (c) 2018 Johannes Kohnen <jwkohnen-github@ko-sys.com>
 *
 * 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 airac

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	format        = "2006-01-02"
	cycleDuration = 24192e11 // 4 weeks
)

var (
	epoch = time.Date(1901, time.January, 10, 0, 0, 0, 0, time.UTC)
)

type (
	// AIRAC represents an Aeronautical Information Regulation And Control
	// (AIRAC) cycle.
	AIRAC uint16

	// Airac is a deprecated alias to AIRAC.
	Airac = AIRAC
)

// Effective returns the effective date of this AIRAC cycle.
func (a AIRAC) Effective() time.Time {
	return epoch.Add(time.Duration(a) * cycleDuration)
}

// Year returns the year for this AIRAC cycle's identifier.
func (a AIRAC) Year() int {
	return a.Effective().Year()
}

// Ordinal returns the ordinal for this AIRAC cycle's identifier.
func (a AIRAC) Ordinal() int {
	return (a.Effective().YearDay()-1)/28 + 1
}

// FromDate returns the AIRAC cycle that occurred at date. A date before the
// internal epoch (1901-01-10) may return wrong data. The upper limit is year
// 2192.
func FromDate(date time.Time) AIRAC {
	a := date.Sub(epoch) / cycleDuration
	return AIRAC(a)
}

// FromString returns an AIRAC cycle that matches the identifier <yyoo>, i.e.
// the last two digits of the year and the ordinal, each with leading zeros.
// This works for years between 1964 and 2063. Identifiers between "6401" and
// "9913" are interpreted as AIRAC cycles between the years 1964 and 1999
// inclusive. AIRAC cycles between "0001" and "6313" are interpreted as AIRAC
// cycles between the years 2000 and 2063 inclusive.
func FromString(yyoo string) (AIRAC, error) {
	year, ordinal, err := parseIdentifier(yyoo)
	if err != nil {
		return 0, err
	}

	lastAiracOfPreviousYear := FromDate(time.Date(year-1, time.December, 31, 0, 0, 0, 0, time.UTC))
	airac := lastAiracOfPreviousYear + AIRAC(ordinal)

	if airac.Year() != year {
		return 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	return airac, nil
}

func parseIdentifier(yyoo string) (year, ordinal int, err error) {
	yyoo = strings.TrimSpace(yyoo)
	if len(yyoo) != 4 {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}
	yyooInt, err := strconv.Atoi(yyoo)
	if err != nil {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	year, ordinal = (yyooInt/100)+1900, yyooInt%100
	if year <= 1963 {
		year += 100
	}
	return year, ordinal, nil
}

// FromStringMust returns an AIRAC cycle that matches the identifier <yyoo>
// like FromString, but does not return an error. If there is an error it will
// panic instead.
func FromStringMust(yyoo string) AIRAC {
	airac, err := FromString(yyoo)
	if err != nil {
		panic(err)
	}
	return airac
}

// String returns a short representation of this AIRAC cycle. "YYOO"
func (a AIRAC) String() string {
	return fmt.Sprintf("%02d%02d", a.Year()%100, a.Ordinal())
}

// LongString returns a verbose representation of this AIRAC cycle.
// "YYOO (effective: YYYY-MM-DD; expires: YYYY-MM-DD)"
func (a AIRAC) LongString() string {
	n := a + 1
	return fmt.Sprintf("%02d%02d (effective: %s; expires: %s)",
		a.Year()%100,
		a.Ordinal(),
		a.Effective().Format(format),
		n.Effective().Add(-1).Format(format),
	)
}

// ByChrono is an []AIRAC wrapper, that satisfies sort.Interface and can be
// used to chronologically sort AIRAC instances.
type ByChrono []AIRAC

// Len ist the number of elements in the collection.
func (c ByChrono) Len() int { return len(c) }

// Less reports whether the element with index i should sort before the element
// with index j.
func (c ByChrono) Less(i, j int) bool { return c[i] < c[j] }

// Swap swaps the elements with indexes i and j.
func (c ByChrono) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

// static assert
var _ sort.Interface = (ByChrono)(nil)
<|endoftext|>


<commit_before>package formatter

import (
	"fmt"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/docker/reference"
	units "github.com/docker/go-units"
)

const (
	defaultImageTableFormat           = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
	defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"

	imageIDHeader    = "IMAGE ID"
	repositoryHeader = "REPOSITORY"
	tagHeader        = "TAG"
	digestHeader     = "DIGEST"
)

// ImageContext contains image specific information required by the formater, encapsulate a Context struct.
type ImageContext struct {
	Context
	Digest bool
}

func isDangling(image types.ImageSummary) bool {
	return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
}

// NewImageFormat returns a format for rendering an ImageContext
func NewImageFormat(source string, quiet bool, digest bool) Format {
	switch source {
	case TableFormatKey:
		switch {
		case quiet:
			return defaultQuietFormat
		case digest:
			return defaultImageTableFormatWithDigest
		default:
			return defaultImageTableFormat
		}
	case RawFormatKey:
		switch {
		case quiet:
			return `image_id: {{.ID}}`
		case digest:
			return `repository: {{ .Repository }}
tag: {{.Tag}}
digest: {{.Digest}}
image_id: {{.ID}}
created_at: {{.CreatedAt}}
virtual_size: {{.Size}}
`
		default:
			return `repository: {{ .Repository }}
tag: {{.Tag}}
image_id: {{.ID}}
created_at: {{.CreatedAt}}
virtual_size: {{.Size}}
`
		}
	}

	format := Format(source)
	if format.IsTable() && digest && !format.Contains("{{.Digest}}") {
		format += "\t{{.Digest}}"
	}
	return format
}

// ImageWrite writes the formatter images using the ImageContext
func ImageWrite(ctx ImageContext, images []types.ImageSummary) error {
	render := func(format func(subContext subContext) error) error {
		return imageFormat(ctx, images, format)
	}
	return ctx.Write(&imageContext{}, render)
}

func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subContext subContext) error) error {
	for _, image := range images {
		images := []*imageContext{}
		if isDangling(image) {
			images = append(images, &imageContext{
				trunc:  ctx.Trunc,
				i:      image,
				repo:   "<none>",
				tag:    "<none>",
				digest: "<none>",
			})
		} else {
			repoTags := map[string][]string{}
			repoDigests := map[string][]string{}

			for _, refString := range append(image.RepoTags) {
				ref, err := reference.ParseNamed(refString)
				if err != nil {
					continue
				}
				if nt, ok := ref.(reference.NamedTagged); ok {
					repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
				}
			}
			for _, refString := range append(image.RepoDigests) {
				ref, err := reference.ParseNamed(refString)
				if err != nil {
					continue
				}
				if c, ok := ref.(reference.Canonical); ok {
					repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
				}
			}

			for repo, tags := range repoTags {
				digests := repoDigests[repo]

				// Do not display digests as their own row
				delete(repoDigests, repo)

				if !ctx.Digest {
					// Ignore digest references, just show tag once
					digests = nil
				}

				for _, tag := range tags {
					if len(digests) == 0 {
						images = append(images, &imageContext{
							trunc:  ctx.Trunc,
							i:      image,
							repo:   repo,
							tag:    tag,
							digest: "<none>",
						})
						continue
					}
					// Display the digests for each tag
					for _, dgst := range digests {
						images = append(images, &imageContext{
							trunc:  ctx.Trunc,
							i:      image,
							repo:   repo,
							tag:    tag,
							digest: dgst,
						})
					}

				}
			}

			// Show rows for remaining digest only references
			for repo, digests := range repoDigests {
				// If digests are displayed, show row per digest
				if ctx.Digest {
					for _, dgst := range digests {
						images = append(images, &imageContext{
							trunc:  ctx.Trunc,
							i:      image,
							repo:   repo,
							tag:    "<none>",
							digest: dgst,
						})
					}
				} else {
					images = append(images, &imageContext{
						trunc: ctx.Trunc,
						i:     image,
						repo:  repo,
						tag:   "<none>",
					})
				}
			}
		}
		for _, imageCtx := range images {
			if err := format(imageCtx); err != nil {
				return err
			}
		}
	}
	return nil
}

type imageContext struct {
	HeaderContext
	trunc  bool
	i      types.ImageSummary
	repo   string
	tag    string
	digest string
}

func (c *imageContext) ID() string {
	c.AddHeader(imageIDHeader)
	if c.trunc {
		return stringid.TruncateID(c.i.ID)
	}
	return c.i.ID
}

func (c *imageContext) Repository() string {
	c.AddHeader(repositoryHeader)
	return c.repo
}

func (c *imageContext) Tag() string {
	c.AddHeader(tagHeader)
	return c.tag
}

func (c *imageContext) Digest() string {
	c.AddHeader(digestHeader)
	return c.digest
}

func (c *imageContext) CreatedSince() string {
	c.AddHeader(createdSinceHeader)
	createdAt := time.Unix(int64(c.i.Created), 0)
	return units.HumanDuration(time.Now().UTC().Sub(createdAt))
}

func (c *imageContext) CreatedAt() string {
	c.AddHeader(createdAtHeader)
	return time.Unix(int64(c.i.Created), 0).String()
}

func (c *imageContext) Size() string {
	c.AddHeader(sizeHeader)
	//NOTE: For backward compatibility we need to return VirtualSize
	return units.HumanSizeWithPrecision(float64(c.i.VirtualSize), 3)
}

func (c *imageContext) Containers() string {
	c.AddHeader(containersHeader)
	if c.i.Containers == -1 {
		return "N/A"
	}
	return fmt.Sprintf("%d", c.i.Containers)
}

func (c *imageContext) VirtualSize() string {
	c.AddHeader(sizeHeader)
	return units.HumanSize(float64(c.i.VirtualSize))
}

func (c *imageContext) SharedSize() string {
	c.AddHeader(sharedSizeHeader)
	if c.i.SharedSize == -1 {
		return "N/A"
	}
	return units.HumanSize(float64(c.i.SharedSize))
}

func (c *imageContext) UniqueSize() string {
	c.AddHeader(uniqueSizeHeader)
	if c.i.Size == -1 {
		return "N/A"
	}
	return units.HumanSize(float64(c.i.Size))
}
<commit_msg>Fix ImageSummary.Size value<commit_after>package formatter

import (
	"fmt"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/docker/reference"
	units "github.com/docker/go-units"
)

const (
	defaultImageTableFormat           = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
	defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"

	imageIDHeader    = "IMAGE ID"
	repositoryHeader = "REPOSITORY"
	tagHeader        = "TAG"
	digestHeader     = "DIGEST"
)

// ImageContext contains image specific information required by the formater, encapsulate a Context struct.
type ImageContext struct {
	Context
	Digest bool
}

func isDangling(image types.ImageSummary) bool {
	return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
}

// NewImageFormat returns a format for rendering an ImageContext
func NewImageFormat(source string, quiet bool, digest bool) Format {
	switch source {
	case TableFormatKey:
		switch {
		case quiet:
			return defaultQuietFormat
		case digest:
			return defaultImageTableFormatWithDigest
		default:
			return defaultImageTableFormat
		}
	case RawFormatKey:
		switch {
		case quiet:
			return `image_id: {{.ID}}`
		case digest:
			return `repository: {{ .Repository }}
tag: {{.Tag}}
digest: {{.Digest}}
image_id: {{.ID}}
created_at: {{.CreatedAt}}
virtual_size: {{.Size}}
`
		default:
			return `repository: {{ .Repository }}
tag: {{.Tag}}
image_id: {{.ID}}
created_at: {{.CreatedAt}}
virtual_size: {{.Size}}
`
		}
	}

	format := Format(source)
	if format.IsTable() && digest && !format.Contains("{{.Digest}}") {
		format += "\t{{.Digest}}"
	}
	return format
}

// ImageWrite writes the formatter images using the ImageContext
func ImageWrite(ctx ImageContext, images []types.ImageSummary) error {
	render := func(format func(subContext subContext) error) error {
		return imageFormat(ctx, images, format)
	}
	return ctx.Write(&imageContext{}, render)
}

func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subContext subContext) error) error {
	for _, image := range images {
		images := []*imageContext{}
		if isDangling(image) {
			images = append(images, &imageContext{
				trunc:  ctx.Trunc,
				i:      image,
				repo:   "<none>",
				tag:    "<none>",
				digest: "<none>",
			})
		} else {
			repoTags := map[string][]string{}
			repoDigests := map[string][]string{}

			for _, refString := range append(image.RepoTags) {
				ref, err := reference.ParseNamed(refString)
				if err != nil {
					continue
				}
				if nt, ok := ref.(reference.NamedTagged); ok {
					repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
				}
			}
			for _, refString := range append(image.RepoDigests) {
				ref, err := reference.ParseNamed(refString)
				if err != nil {
					continue
				}
				if c, ok := ref.(reference.Canonical); ok {
					repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
				}
			}

			for repo, tags := range repoTags {
				digests := repoDigests[repo]

				// Do not display digests as their own row
				delete(repoDigests, repo)

				if !ctx.Digest {
					// Ignore digest references, just show tag once
					digests = nil
				}

				for _, tag := range tags {
					if len(digests) == 0 {
						images = append(images, &imageContext{
							trunc:  ctx.Trunc,
							i:      image,
							repo:   repo,
							tag:    tag,
							digest: "<none>",
						})
						continue
					}
					// Display the digests for each tag
					for _, dgst := range digests {
						images = append(images, &imageContext{
							trunc:  ctx.Trunc,
							i:      image,
							repo:   repo,
							tag:    tag,
							digest: dgst,
						})
					}

				}
			}

			// Show rows for remaining digest only references
			for repo, digests := range repoDigests {
				// If digests are displayed, show row per digest
				if ctx.Digest {
					for _, dgst := range digests {
						images = append(images, &imageContext{
							trunc:  ctx.Trunc,
							i:      image,
							repo:   repo,
							tag:    "<none>",
							digest: dgst,
						})
					}
				} else {
					images = append(images, &imageContext{
						trunc: ctx.Trunc,
						i:     image,
						repo:  repo,
						tag:   "<none>",
					})
				}
			}
		}
		for _, imageCtx := range images {
			if err := format(imageCtx); err != nil {
				return err
			}
		}
	}
	return nil
}

type imageContext struct {
	HeaderContext
	trunc  bool
	i      types.ImageSummary
	repo   string
	tag    string
	digest string
}

func (c *imageContext) ID() string {
	c.AddHeader(imageIDHeader)
	if c.trunc {
		return stringid.TruncateID(c.i.ID)
	}
	return c.i.ID
}

func (c *imageContext) Repository() string {
	c.AddHeader(repositoryHeader)
	return c.repo
}

func (c *imageContext) Tag() string {
	c.AddHeader(tagHeader)
	return c.tag
}

func (c *imageContext) Digest() string {
	c.AddHeader(digestHeader)
	return c.digest
}

func (c *imageContext) CreatedSince() string {
	c.AddHeader(createdSinceHeader)
	createdAt := time.Unix(int64(c.i.Created), 0)
	return units.HumanDuration(time.Now().UTC().Sub(createdAt))
}

func (c *imageContext) CreatedAt() string {
	c.AddHeader(createdAtHeader)
	return time.Unix(int64(c.i.Created), 0).String()
}

func (c *imageContext) Size() string {
	c.AddHeader(sizeHeader)
	return units.HumanSizeWithPrecision(float64(c.i.Size), 3)
}

func (c *imageContext) Containers() string {
	c.AddHeader(containersHeader)
	if c.i.Containers == -1 {
		return "N/A"
	}
	return fmt.Sprintf("%d", c.i.Containers)
}

func (c *imageContext) VirtualSize() string {
	c.AddHeader(sizeHeader)
	return units.HumanSize(float64(c.i.VirtualSize))
}

func (c *imageContext) SharedSize() string {
	c.AddHeader(sharedSizeHeader)
	if c.i.SharedSize == -1 {
		return "N/A"
	}
	return units.HumanSize(float64(c.i.SharedSize))
}

func (c *imageContext) UniqueSize() string {
	c.AddHeader(uniqueSizeHeader)
	if c.i.VirtualSize == -1 || c.i.SharedSize == -1 {
		return "N/A"
	}
	return units.HumanSize(float64(c.i.VirtualSize - c.i.SharedSize))
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"strings"

	"github.com/hashicorp/nomad/api/contexts"
	"github.com/posener/complete"
)

type JobDeploymentsCommand struct {
	Meta
}

func (c *JobDeploymentsCommand) Help() string {
	helpText := `
Usage: nomad job deployments [options] <job>

  Deployments is used to display the deployments for a particular job.

General Options:

  ` + generalOptionsUsage() + `

Deployments Options:

  -json
    Output the deployments in a JSON format.

  -t
    Format and display deployments using a Go template.

  -latest
    Display the latest deployment only.

  -verbose
    Display full information.
`
	return strings.TrimSpace(helpText)
}

func (c *JobDeploymentsCommand) Synopsis() string {
	return "List deployments for a job"
}

func (c *JobDeploymentsCommand) AutocompleteFlags() complete.Flags {
	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
		complete.Flags{
			"-json":    complete.PredictNothing,
			"-t":       complete.PredictAnything,
			"-latest":  complete.PredictNothing,
			"-verbose": complete.PredictNothing,
		})
}

func (c *JobDeploymentsCommand) AutocompleteArgs() complete.Predictor {
	return complete.PredictFunc(func(a complete.Args) []string {
		client, err := c.Meta.Client()
		if err != nil {
			return nil
		}

		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Jobs, nil)
		if err != nil {
			return []string{}
		}
		return resp.Matches[contexts.Jobs]
	})
}

func (c *JobDeploymentsCommand) Name() string { return "job deployments" }

func (c *JobDeploymentsCommand) Run(args []string) int {
	var json, latest, verbose, all bool
	var tmpl string

	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&latest, "latest", false, "")
	flags.BoolVar(&verbose, "verbose", false, "")
	flags.BoolVar(&all, "all", false, "")
	flags.BoolVar(&json, "json", false, "")
	flags.StringVar(&tmpl, "t", "", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Check that we got exactly one node
	args = flags.Args()
	if l := len(args); l != 1 {
		c.Ui.Error("This command takes one argument: <job>")
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	jobID := args[0]

	// Check if the job exists
	jobs, _, err := client.Jobs().PrefixList(jobID)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error listing jobs: %s", err))
		return 1
	}
	if len(jobs) == 0 {
		c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
		return 1
	}
	if len(jobs) > 1 && strings.TrimSpace(jobID) != jobs[0].ID {
		c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs)))
		return 1
	}
	jobID = jobs[0].ID

	// Truncate the id unless full length is requested
	length := shortId
	if verbose {
		length = fullId
	}

	if latest {
		deploy, _, err := client.Jobs().LatestDeployment(jobID, nil)
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error retrieving deployments: %s", err))
			return 1
		}

		if json || len(tmpl) > 0 {
			out, err := Format(json, tmpl, deploy)
			if err != nil {
				c.Ui.Error(err.Error())
				return 1
			}

			c.Ui.Output(out)
			return 0
		}

		c.Ui.Output(c.Colorize().Color(formatDeployment(deploy, length)))
		return 0
	}

	deploys, _, err := client.Jobs().Deployments(jobID, all, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error retrieving deployments: %s", err))
		return 1
	}

	if json || len(tmpl) > 0 {
		out, err := Format(json, tmpl, deploys)
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}

		c.Ui.Output(out)
		return 0
	}

	c.Ui.Output(formatDeployments(deploys, length))
	return 0
}
<commit_msg>Add -all to help text and flags<commit_after>package command

import (
	"fmt"
	"strings"

	"github.com/hashicorp/nomad/api/contexts"
	"github.com/posener/complete"
)

type JobDeploymentsCommand struct {
	Meta
}

func (c *JobDeploymentsCommand) Help() string {
	helpText := `
Usage: nomad job deployments [options] <job>

  Deployments is used to display the deployments for a particular job.

General Options:

  ` + generalOptionsUsage() + `

Deployments Options:

  -json
    Output the deployments in a JSON format.

  -t
    Format and display deployments using a Go template.

  -latest
    Display the latest deployment only.

  -verbose
    Display full information.

  -all-allocs
    Display all deployments matching the job ID, including those
    from an older instance of the job.
`
	return strings.TrimSpace(helpText)
}

func (c *JobDeploymentsCommand) Synopsis() string {
	return "List deployments for a job"
}

func (c *JobDeploymentsCommand) AutocompleteFlags() complete.Flags {
	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
		complete.Flags{
			"-json":    complete.PredictNothing,
			"-t":       complete.PredictAnything,
			"-latest":  complete.PredictNothing,
			"-verbose": complete.PredictNothing,
			"-all":     complete.PredictNothing,
		})
}

func (c *JobDeploymentsCommand) AutocompleteArgs() complete.Predictor {
	return complete.PredictFunc(func(a complete.Args) []string {
		client, err := c.Meta.Client()
		if err != nil {
			return nil
		}

		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Jobs, nil)
		if err != nil {
			return []string{}
		}
		return resp.Matches[contexts.Jobs]
	})
}

func (c *JobDeploymentsCommand) Name() string { return "job deployments" }

func (c *JobDeploymentsCommand) Run(args []string) int {
	var json, latest, verbose, all bool
	var tmpl string

	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&latest, "latest", false, "")
	flags.BoolVar(&verbose, "verbose", false, "")
	flags.BoolVar(&all, "all", false, "")
	flags.BoolVar(&json, "json", false, "")
	flags.StringVar(&tmpl, "t", "", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Check that we got exactly one node
	args = flags.Args()
	if l := len(args); l != 1 {
		c.Ui.Error("This command takes one argument: <job>")
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	jobID := args[0]

	// Check if the job exists
	jobs, _, err := client.Jobs().PrefixList(jobID)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error listing jobs: %s", err))
		return 1
	}
	if len(jobs) == 0 {
		c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
		return 1
	}
	if len(jobs) > 1 && strings.TrimSpace(jobID) != jobs[0].ID {
		c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs)))
		return 1
	}
	jobID = jobs[0].ID

	// Truncate the id unless full length is requested
	length := shortId
	if verbose {
		length = fullId
	}

	if latest {
		deploy, _, err := client.Jobs().LatestDeployment(jobID, nil)
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error retrieving deployments: %s", err))
			return 1
		}

		if json || len(tmpl) > 0 {
			out, err := Format(json, tmpl, deploy)
			if err != nil {
				c.Ui.Error(err.Error())
				return 1
			}

			c.Ui.Output(out)
			return 0
		}

		c.Ui.Output(c.Colorize().Color(formatDeployment(deploy, length)))
		return 0
	}

	deploys, _, err := client.Jobs().Deployments(jobID, all, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error retrieving deployments: %s", err))
		return 1
	}

	if json || len(tmpl) > 0 {
		out, err := Format(json, tmpl, deploys)
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}

		c.Ui.Output(out)
		return 0
	}

	c.Ui.Output(formatDeployments(deploys, length))
	return 0
}
<|endoftext|>


<commit_before>package v7

import (
	"os"
	"os/signal"
	"time"

	"code.cloudfoundry.org/cli/actor/actionerror"
	"code.cloudfoundry.org/cli/actor/sharedaction"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/flag"
	"code.cloudfoundry.org/cli/command/v7/shared"
)

type LogsCommand struct {
	BaseCommand

	RequiredArgs    flag.AppName `positional-args:"yes"`
	Recent          bool         `long:"recent" description:"Dump recent logs instead of tailing"`
	usage           interface{}  `usage:"CF_NAME logs APP_NAME"`
	relatedCommands interface{}  `related_commands:"app, apps, ssh"`

	CC_Client      *ccv3.Client
	LogCacheClient sharedaction.LogCacheClient
}

func (cmd *LogsCommand) Setup(config command.Config, ui command.UI) error {
	err := cmd.BaseCommand.Setup(config, ui)
	if err != nil {
		return err
	}

	ccClient, _, err := shared.GetNewClientsAndConnectToCF(config, ui, "")
	if err != nil {
		return err
	}

	cmd.LogCacheClient = command.NewLogCacheClient(ccClient.Info.LogCache(), config, ui)
	return nil
}

func (cmd LogsCommand) Execute(args []string) error {
	err := cmd.SharedActor.CheckTarget(true, true)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return err
	}

	cmd.UI.DisplayTextWithFlavor("Retrieving logs for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...",
		map[string]interface{}{
			"AppName":   cmd.RequiredArgs.AppName,
			"OrgName":   cmd.Config.TargetedOrganization().Name,
			"SpaceName": cmd.Config.TargetedSpace().Name,
			"Username":  user.Name,
		})
	cmd.UI.DisplayNewline()

	if cmd.Recent {
		return cmd.displayRecentLogs()
	}

	stop := make(chan struct{})
	stoppedRefreshing := make(chan struct{})
	stoppedOutputtingRefreshErrors := make(chan struct{})
	err = cmd.refreshTokenPeriodically(stop, stoppedRefreshing, stoppedOutputtingRefreshErrors)
	if err != nil {
		return err
	}

	err = cmd.streamLogs()

	close(stop)
	<-stoppedRefreshing
	<-stoppedOutputtingRefreshErrors

	return err
}

func (cmd LogsCommand) displayRecentLogs() error {
	messages, warnings, err := cmd.Actor.GetRecentLogsForApplicationByNameAndSpace(
		cmd.RequiredArgs.AppName,
		cmd.Config.TargetedSpace().GUID,
		cmd.LogCacheClient,
	)

	for _, message := range messages {
		cmd.UI.DisplayLogMessage(message, true)
	}

	cmd.UI.DisplayWarnings(warnings)
	return err
}

func (cmd LogsCommand) refreshTokenPeriodically(
	stop chan struct{},
	stoppedRefreshing chan struct{},
	stoppedOutputtingRefreshErrors chan struct{}) error {

	tokenRefreshErrors, err := cmd.Actor.ScheduleTokenRefresh(time.After, stop, stoppedRefreshing)
	if err != nil {
		return err
	}

	go func() {
		defer close(stoppedOutputtingRefreshErrors)

		for {
			select {
			case err := <-tokenRefreshErrors:
				cmd.UI.DisplayError(err)
			case <-stop:
				return
			}
		}
	}()

	return nil
}

func (cmd LogsCommand) handleLogErr(logErr error) {
	switch logErr.(type) {
	case actionerror.LogCacheTimeoutError:
		cmd.UI.DisplayWarning("timeout connecting to log server, no log will be shown")
	default:
		cmd.UI.DisplayWarning("Failed to retrieve logs from Log Cache: {{.Error}}", map[string]interface{}{
			"Error": logErr,
		})
	}
}

func (cmd LogsCommand) streamLogs() error {
	messages, logErrs, stopStreaming, warnings, err := cmd.Actor.GetStreamingLogsForApplicationByNameAndSpace(
		cmd.RequiredArgs.AppName,
		cmd.Config.TargetedSpace().GUID,
		cmd.LogCacheClient,
	)

	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return err
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	defer stopStreaming()
	var messagesClosed, errLogsClosed bool
	for {
		select {
		case message, ok := <-messages:
			if !ok {
				messagesClosed = true
				break
			}
			cmd.UI.DisplayLogMessage(message, true)
		case logErr, ok := <-logErrs:
			if !ok {
				errLogsClosed = true
				break
			}
			cmd.handleLogErr(logErr)
		case <-c:
			return nil
		}

		if messagesClosed && errLogsClosed {
			break
		}
	}

	return nil
}
<commit_msg>Revert "Check output of BaseCommand in logs command"<commit_after>package v7

import (
	"os"
	"os/signal"
	"time"

	"code.cloudfoundry.org/cli/actor/actionerror"
	"code.cloudfoundry.org/cli/actor/sharedaction"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/flag"
	"code.cloudfoundry.org/cli/command/v7/shared"
)

type LogsCommand struct {
	BaseCommand

	RequiredArgs    flag.AppName `positional-args:"yes"`
	Recent          bool         `long:"recent" description:"Dump recent logs instead of tailing"`
	usage           interface{}  `usage:"CF_NAME logs APP_NAME"`
	relatedCommands interface{}  `related_commands:"app, apps, ssh"`

	CC_Client      *ccv3.Client
	LogCacheClient sharedaction.LogCacheClient
}

func (cmd *LogsCommand) Setup(config command.Config, ui command.UI) error {
	cmd.BaseCommand.Setup(config, ui)
	ccClient, _, err := shared.GetNewClientsAndConnectToCF(config, ui, "")
	if err != nil {
		return err
	}

	cmd.LogCacheClient = command.NewLogCacheClient(ccClient.Info.LogCache(), config, ui)
	return nil
}

func (cmd LogsCommand) Execute(args []string) error {
	err := cmd.SharedActor.CheckTarget(true, true)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return err
	}

	cmd.UI.DisplayTextWithFlavor("Retrieving logs for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...",
		map[string]interface{}{
			"AppName":   cmd.RequiredArgs.AppName,
			"OrgName":   cmd.Config.TargetedOrganization().Name,
			"SpaceName": cmd.Config.TargetedSpace().Name,
			"Username":  user.Name,
		})
	cmd.UI.DisplayNewline()

	if cmd.Recent {
		return cmd.displayRecentLogs()
	}

	stop := make(chan struct{})
	stoppedRefreshing := make(chan struct{})
	stoppedOutputtingRefreshErrors := make(chan struct{})
	err = cmd.refreshTokenPeriodically(stop, stoppedRefreshing, stoppedOutputtingRefreshErrors)
	if err != nil {
		return err
	}

	err = cmd.streamLogs()

	close(stop)
	<-stoppedRefreshing
	<-stoppedOutputtingRefreshErrors

	return err
}

func (cmd LogsCommand) displayRecentLogs() error {
	messages, warnings, err := cmd.Actor.GetRecentLogsForApplicationByNameAndSpace(
		cmd.RequiredArgs.AppName,
		cmd.Config.TargetedSpace().GUID,
		cmd.LogCacheClient,
	)

	for _, message := range messages {
		cmd.UI.DisplayLogMessage(message, true)
	}

	cmd.UI.DisplayWarnings(warnings)
	return err
}

func (cmd LogsCommand) refreshTokenPeriodically(
	stop chan struct{},
	stoppedRefreshing chan struct{},
	stoppedOutputtingRefreshErrors chan struct{}) error {

	tokenRefreshErrors, err := cmd.Actor.ScheduleTokenRefresh(time.After, stop, stoppedRefreshing)
	if err != nil {
		return err
	}

	go func() {
		defer close(stoppedOutputtingRefreshErrors)

		for {
			select {
			case err := <-tokenRefreshErrors:
				cmd.UI.DisplayError(err)
			case <-stop:
				return
			}
		}
	}()

	return nil
}

func (cmd LogsCommand) handleLogErr(logErr error) {
	switch logErr.(type) {
	case actionerror.LogCacheTimeoutError:
		cmd.UI.DisplayWarning("timeout connecting to log server, no log will be shown")
	default:
		cmd.UI.DisplayWarning("Failed to retrieve logs from Log Cache: {{.Error}}", map[string]interface{}{
			"Error": logErr,
		})
	}
}

func (cmd LogsCommand) streamLogs() error {
	messages, logErrs, stopStreaming, warnings, err := cmd.Actor.GetStreamingLogsForApplicationByNameAndSpace(
		cmd.RequiredArgs.AppName,
		cmd.Config.TargetedSpace().GUID,
		cmd.LogCacheClient,
	)

	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return err
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	defer stopStreaming()
	var messagesClosed, errLogsClosed bool
	for {
		select {
		case message, ok := <-messages:
			if !ok {
				messagesClosed = true
				break
			}
			cmd.UI.DisplayLogMessage(message, true)
		case logErr, ok := <-logErrs:
			if !ok {
				errLogsClosed = true
				break
			}
			cmd.handleLogErr(logErr)
		case <-c:
			return nil
		}

		if messagesClosed && errLogsClosed {
			break
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	statusCmd = &cobra.Command{
		Use: "status",
		Run: statusCommand,
	}
	porcelain = false
)

func statusCommand(cmd *cobra.Command, args []string) {
	requireInRepo()

	ref, err := git.CurrentRef()
	if err != nil {
		Panic(err, "Could not get the current ref")
	}

	stagedPointers, err := lfs.ScanIndex()
	if err != nil {
		Panic(err, "Could not scan staging for Git LFS objects")
	}

	if porcelain {
		for _, p := range stagedPointers {
			switch p.Status {
			case "R", "C":
				Print("%s  %s -> %s %d", p.Status, p.SrcName, p.Name, p.Size)
			case "M":
				Print(" %s %s %d", p.Status, p.Name, p.Size)
			default:
				Print("%s  %s %d", p.Status, p.Name, p.Size)
			}
		}
		return
	}

	branch, err := git.CurrentBranch()
	if err != nil {
		Panic(err, "Could not get current branch")
	}
	Print("On branch %s", branch)

	remoteRef, err := git.CurrentRemoteRef()
	if err == nil {

		pointers, err := lfs.ScanRefs(ref.Sha, "^"+remoteRef.Sha, nil)
		if err != nil {
			Panic(err, "Could not scan for Git LFS objects")
		}

		Print("Git LFS objects to be pushed to %s:\n", remoteRef.Name)
		for _, p := range pointers {
			Print("\t%s (%s)", p.Name, humanizeBytes(p.Size))
		}
	}

	Print("\nGit LFS objects to be committed:\n")
	for _, p := range stagedPointers {
		switch p.Status {
		case "R", "C":
			Print("\t%s -> %s (%s)", p.SrcName, p.Name, humanizeBytes(p.Size))
		case "M":
		default:
			Print("\t%s (%s)", p.Name, humanizeBytes(p.Size))
		}
	}

	Print("\nGit LFS objects not staged for commit:\n")
	for _, p := range stagedPointers {
		if p.Status == "M" {
			Print("\t%s", p.Name)
		}
	}

	Print("")
}

var byteUnits = []string{"B", "KB", "MB", "GB", "TB"}

func humanizeBytes(bytes int64) string {
	var output string
	size := float64(bytes)

	if bytes < 1024 {
		return fmt.Sprintf("%d B", bytes)
	}

	for _, unit := range byteUnits {
		if size < 1024.0 {
			output = fmt.Sprintf("%3.1f %s", size, unit)
			break
		}
		size /= 1024.0
	}
	return output
}

func init() {
	statusCmd.Flags().BoolVarP(&porcelain, "porcelain", "p", false, "Give the output in an easy-to-parse format for scripts.")
	RootCmd.AddCommand(statusCmd)
}
<commit_msg>remove unnecessary git.CurrentBranch() call in status command<commit_after>package commands

import (
	"fmt"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	statusCmd = &cobra.Command{
		Use: "status",
		Run: statusCommand,
	}
	porcelain = false
)

func statusCommand(cmd *cobra.Command, args []string) {
	requireInRepo()

	ref, err := git.CurrentRef()
	if err != nil {
		Panic(err, "Could not get the current ref")
	}

	stagedPointers, err := lfs.ScanIndex()
	if err != nil {
		Panic(err, "Could not scan staging for Git LFS objects")
	}

	if porcelain {
		for _, p := range stagedPointers {
			switch p.Status {
			case "R", "C":
				Print("%s  %s -> %s %d", p.Status, p.SrcName, p.Name, p.Size)
			case "M":
				Print(" %s %s %d", p.Status, p.Name, p.Size)
			default:
				Print("%s  %s %d", p.Status, p.Name, p.Size)
			}
		}
		return
	}

	Print("On branch %s", ref.Name)

	remoteRef, err := git.CurrentRemoteRef()
	if err == nil {

		pointers, err := lfs.ScanRefs(ref.Sha, "^"+remoteRef.Sha, nil)
		if err != nil {
			Panic(err, "Could not scan for Git LFS objects")
		}

		Print("Git LFS objects to be pushed to %s:\n", remoteRef.Name)
		for _, p := range pointers {
			Print("\t%s (%s)", p.Name, humanizeBytes(p.Size))
		}
	}

	Print("\nGit LFS objects to be committed:\n")
	for _, p := range stagedPointers {
		switch p.Status {
		case "R", "C":
			Print("\t%s -> %s (%s)", p.SrcName, p.Name, humanizeBytes(p.Size))
		case "M":
		default:
			Print("\t%s (%s)", p.Name, humanizeBytes(p.Size))
		}
	}

	Print("\nGit LFS objects not staged for commit:\n")
	for _, p := range stagedPointers {
		if p.Status == "M" {
			Print("\t%s", p.Name)
		}
	}

	Print("")
}

var byteUnits = []string{"B", "KB", "MB", "GB", "TB"}

func humanizeBytes(bytes int64) string {
	var output string
	size := float64(bytes)

	if bytes < 1024 {
		return fmt.Sprintf("%d B", bytes)
	}

	for _, unit := range byteUnits {
		if size < 1024.0 {
			output = fmt.Sprintf("%3.1f %s", size, unit)
			break
		}
		size /= 1024.0
	}
	return output
}

func init() {
	statusCmd.Flags().BoolVarP(&porcelain, "porcelain", "p", false, "Give the output in an easy-to-parse format for scripts.")
	RootCmd.AddCommand(statusCmd)
}
<|endoftext|>


<commit_before>//
package engine

import (
	"archive/tar"
	"bytes"
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"time"

	api "github.com/nanobox-io/nanobox-api-client"
	"github.com/nanobox-io/nanobox-golang-stylish"
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/config"
	engineutil "github.com/nanobox-io/nanobox/util/engine"
	fileutil "github.com/nanobox-io/nanobox/util/file"
	s3util "github.com/nanobox-io/nanobox/util/s3"
)

var tw *tar.Writer

//
var publishCmd = &cobra.Command{
	Use:   "publish",
	Short: "Publishes an engine to nanobox.io",
	Long:  ``,

	Run: publish,
}

// publish
func publish(ccmd *cobra.Command, args []string) {
	stylish.Header("publishing engine")

	//
	api.UserSlug, api.AuthToken = Auth.Authenticate()

	// create a new release
	fmt.Printf(stylish.Bullet("Creating release..."))
	release := &api.EngineRelease{}

	// create an annonymous struct to hold data that doesn't relate to a release but
	// is needed as part of the publish process
	opts := &struct {
		Generic  bool     `json:"generic"`
		Language string   `json:"language"`
		Overlays []string `json:"overlays"`
	}{}

	// ensure there is an Enginefile
	if _, err := os.Stat("./Enginefile"); err != nil {
		fmt.Printf("Enginefile not found. Be sure to publish from a project directory. Exiting...\n")
		os.Exit(1)
	}

	// parse the ./Enginefile into the new release
	if err := Config.ParseConfig("./Enginefile", release); err != nil {
		fmt.Printf("Nanobox failed to parse your Enginefile. Please ensure it is valid YAML and try again.\n")
		os.Exit(1)
	}

	// parse the ./Enginefile again to get the remaining fields
	if err := Config.ParseConfig("./Enginefile", opts); err != nil {
		fmt.Printf("Nanobox failed to parse your Enginefile. Please ensure it is valid YAML and try again.\n")
		os.Exit(1)
	}

	fmt.Printf(stylish.Bullet("Verifying engine is publishable..."))

	// determine if any required fields (name, version, language, summary) are missing,
	// if any are found to be missing exit 1
	// NOTE: I do this using fallthrough for asthetics onlye. The message is generic
	// enough that all cases will return the same message, and this looks better than
	// a single giant case (var == "" || var == "" || ...)
	switch {
	case opts.Language == "":
		fallthrough
	case release.Name == "":
		fallthrough
	case release.Summary == "":
		fallthrough
	case release.Version == "":
		fmt.Printf(stylish.Error("required fields missing", `Your Enginefile is missing one or more of the following required fields for publishing:

  name:      # the name of your project
  version:   # the current version of the project
  language:  # the lanauge (ruby, golang, etc.) of the engine
  summary:   # a 140 character summary of the project

Please ensure all required fields are provided and try again.`))

		os.Exit(1)
	}

	// attempt to read a README.md file and add it to the release...
	b, err := ioutil.ReadFile("./README.md")
	if err != nil {

		// this only fails if the file is not found, EOF is not an error. If no Readme
		// is found exit 1
		fmt.Printf(stylish.Error("missing readme", "Your engine is missing a README.md file. This file is required for publishing, as it is the only way for you to communicate how to use your engine. Please add a README.md and try again."))
		os.Exit(1)
	}

	//
	release.Readme = string(b)

	// check to see if the engine already exists on nanobox.io
	fmt.Printf(stylish.Bullet("Checking for existing engine on nanobox.io"))
	engine, err := api.GetEngine(api.UserSlug, release.Name)

	// if no engine exists, create a new one
	if err != nil {

		// if no engine is found create one
		if apiErr, _ := err.(api.APIError); apiErr.Code == 404 {

			fmt.Printf(stylish.SubTaskStart("Creating new engine on nanobox.io"))

			//
			engine = &api.Engine{
				Generic:      opts.Generic,
				LanguageName: opts.Language,
				Name:         release.Name,
			}

			//
			if _, err := api.CreateEngine(engine); err != nil {
				fmt.Printf(stylish.ErrBullet("Unable to create engine (%v).", err))
				os.Exit(1)
			}

			// wait until engine has been successfuly created before uploading to s3
			for {
				fmt.Print(".")

				p, err := api.GetEngine(api.UserSlug, release.Name)
				if err != nil {
					Config.Fatal("[commands/publish] api.GetEngine failed", err.Error())
				}

				// once the engine is "active", break
				if p.State == "active" {
					break
				}

				//
				time.Sleep(1000 * time.Millisecond)
			}

			// generically handle any other errors
		} else {
			Config.Fatal("[commands/publish] api.GetEngine failed", err.Error())
		}

		stylish.Success()
	}

	// create a meta.json file where we can add any extra data we might need; since
	// this is only used for internal purposes the file is removed once we're done
	// with it
	meta, err := os.Create("./meta.json")
	if err != nil {
		Config.Fatal("[commands/publish] os.Create() failed", err.Error())
	}
	defer meta.Close()
	defer os.Remove(meta.Name())

	// add any custom info to the metafile
	meta.WriteString(fmt.Sprintf(`{"engine_id": "%s"}`, engine.ID))

	// this is our predefined list of everything that gets archived as part of the
	// engine being published
	files := map[string][]string{
		"required": []string{"./bin", "./Enginefile", "./meta.json"},
		"optional": []string{"./lib", "./templates", "./files"},
	}

	// check to ensure no required files are missing
	for k, v := range files {
		if k == "required" {
			for _, f := range v {
				if _, err := os.Stat(f); err != nil {
					fmt.Printf(stylish.Error("required files missing", "Your Engine is missing one or more required files for publishing. Please read the following documentation to ensure all required files are included and try again.:\n\ndocs.nanobox.io/engines/project-creation/#example-engine-file-structure\n"))
					os.Exit(1)
				}
			}
		}
	}

	// create the temp engines folder for building the tarball
	tarPath := filepath.Join(config.EnginesDir, release.Name)
	if err := os.Mkdir(tarPath, 0755); err != nil {
		Config.Fatal("[commands/engine/publish] os.Create() failed", err.Error())
	}

	// remove tarDir once published
	defer func() {
		if err := os.RemoveAll(tarPath); err != nil {
			os.Stderr.WriteString(stylish.ErrBullet("Faild to remove '%v'...", tarPath))
		}
	}()

	// parse the ./Enginefile again to get the overlays
	if err := Config.ParseConfig("./Enginefile", opts); err != nil {
		fmt.Printf("Nanobox failed to parse your Enginefile. Please ensure it is valid YAML and try again.\n")
		os.Exit(1)
	}

	// iterate through each overlay fetching it and untaring to the tar path
	for _, overlay := range opts.Overlays {
		engineutil.GetOverlay(overlay, tarPath)
	}

	// range over each file from each file type, building the final list of files
	// to be tarballed
	for _, v := range files {
		for _, f := range v {

			// not handling error here because an error simply means the file doesn't
			// exist and therefor wont be copied to the final tarball
			fileutil.Copy(f, tarPath)
		}
	}

	// create an empty buffer for writing the file contents to for the subsequent
	// upload
	archive := bytes.NewBuffer(nil)

	//
	h := md5.New()

	//
	if err := fileutil.Tar(tarPath, archive, h); err != nil {
		Config.Fatal("[commands/engine/publish] file.Tar() failed", err.Error())
	}

	// add the checksum for the new release once its finished being archived
	release.Checksum = fmt.Sprintf("%x", h.Sum(nil))

	//
	// attempt to upload the release to S3
	fmt.Printf(stylish.Bullet("Uploading release to s3..."))

	v := url.Values{}
	v.Add("user_slug", api.UserSlug)
	v.Add("auth_token", api.AuthToken)
	v.Add("version", release.Version)

	//
	s3url, err := s3util.RequestURL(fmt.Sprintf("http://api.nanobox.io/v1/engines/%v/request_upload?%v", release.Name, v.Encode()))
	if err != nil {
		Config.Fatal("[commands/publish] s3.RequestURL() failed", err.Error())
	}

	//
	if err := s3util.Upload(s3url, archive); err != nil {
		Config.Fatal("[commands/publish] s3.Upload() failed", err.Error())
	}

	//
	// if the release uploaded successfully to s3, created one on odin
	fmt.Printf(stylish.Bullet("Uploading release to nanobox.io"))
	if _, err := api.CreateEngineRelease(release.Name, release); err != nil {
		fmt.Printf(stylish.ErrBullet("Unable to publish release (%v).", err))
		os.Exit(1)
	}
}
<commit_msg>Fix small typo in engine publish.<commit_after>//
package engine

import (
	"archive/tar"
	"bytes"
	"crypto/md5"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"time"

	api "github.com/nanobox-io/nanobox-api-client"
	"github.com/nanobox-io/nanobox-golang-stylish"
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/config"
	engineutil "github.com/nanobox-io/nanobox/util/engine"
	fileutil "github.com/nanobox-io/nanobox/util/file"
	s3util "github.com/nanobox-io/nanobox/util/s3"
)

var tw *tar.Writer

//
var publishCmd = &cobra.Command{
	Use:   "publish",
	Short: "Publishes an engine to nanobox.io",
	Long:  ``,

	Run: publish,
}

// publish
func publish(ccmd *cobra.Command, args []string) {
	stylish.Header("publishing engine")

	//
	api.UserSlug, api.AuthToken = Auth.Authenticate()

	// create a new release
	fmt.Printf(stylish.Bullet("Creating release..."))
	release := &api.EngineRelease{}

	// create an annonymous struct to hold data that doesn't relate to a release but
	// is needed as part of the publish process
	opts := &struct {
		Generic  bool     `json:"generic"`
		Language string   `json:"language"`
		Overlays []string `json:"overlays"`
	}{}

	// ensure there is an Enginefile
	if _, err := os.Stat("./Enginefile"); err != nil {
		fmt.Printf("Enginefile not found. Be sure to publish from a project directory. Exiting...\n")
		os.Exit(1)
	}

	// parse the ./Enginefile into the new release
	if err := Config.ParseConfig("./Enginefile", release); err != nil {
		fmt.Printf("Nanobox failed to parse your Enginefile. Please ensure it is valid YAML and try again.\n")
		os.Exit(1)
	}

	// parse the ./Enginefile again to get the remaining fields
	if err := Config.ParseConfig("./Enginefile", opts); err != nil {
		fmt.Printf("Nanobox failed to parse your Enginefile. Please ensure it is valid YAML and try again.\n")
		os.Exit(1)
	}

	fmt.Printf(stylish.Bullet("Verifying engine is publishable..."))

	// determine if any required fields (name, version, language, summary) are missing,
	// if any are found to be missing exit 1
	// NOTE: I do this using fallthrough for asthetics onlye. The message is generic
	// enough that all cases will return the same message, and this looks better than
	// a single giant case (var == "" || var == "" || ...)
	switch {
	case opts.Language == "":
		fallthrough
	case release.Name == "":
		fallthrough
	case release.Summary == "":
		fallthrough
	case release.Version == "":
		fmt.Printf(stylish.Error("required fields missing", `Your Enginefile is missing one or more of the following required fields for publishing:

  name:      # the name of your project
  version:   # the current version of the project
  language:  # the lanauge (ruby, golang, etc.) of the engine
  summary:   # a 140 character summary of the project

Please ensure all required fields are provided and try again.`))

		os.Exit(1)
	}

	// attempt to read a README.md file and add it to the release...
	b, err := ioutil.ReadFile("./README.md")
	if err != nil {

		// this only fails if the file is not found, EOF is not an error. If no Readme
		// is found exit 1
		fmt.Printf(stylish.Error("missing readme", "Your engine is missing a README.md file. This file is required for publishing, as it is the only way for you to communicate how to use your engine. Please add a README.md and try again."))
		os.Exit(1)
	}

	//
	release.Readme = string(b)

	// check to see if the engine already exists on nanobox.io
	fmt.Printf(stylish.Bullet("Checking for existing engine on nanobox.io"))
	engine, err := api.GetEngine(api.UserSlug, release.Name)

	// if no engine exists, create a new one
	if err != nil {

		// if no engine is found create one
		if apiErr, _ := err.(api.APIError); apiErr.Code == 404 {

			fmt.Printf(stylish.SubTaskStart("Creating new engine on nanobox.io"))

			//
			engine = &api.Engine{
				Generic:      opts.Generic,
				LanguageName: opts.Language,
				Name:         release.Name,
			}

			//
			if _, err := api.CreateEngine(engine); err != nil {
				fmt.Printf(stylish.ErrBullet("Unable to create engine (%v).", err))
				os.Exit(1)
			}

			// wait until engine has been successfuly created before uploading to s3
			for {
				fmt.Print(".")

				p, err := api.GetEngine(api.UserSlug, release.Name)
				if err != nil {
					Config.Fatal("[commands/publish] api.GetEngine failed", err.Error())
				}

				// once the engine is "active", break
				if p.State == "active" {
					break
				}

				//
				time.Sleep(1000 * time.Millisecond)
			}

			// generically handle any other errors
		} else {
			Config.Fatal("[commands/publish] api.GetEngine failed", err.Error())
		}

		stylish.Success()
	}

	// create a meta.json file where we can add any extra data we might need; since
	// this is only used for internal purposes the file is removed once we're done
	// with it
	meta, err := os.Create("./meta.json")
	if err != nil {
		Config.Fatal("[commands/publish] os.Create() failed", err.Error())
	}
	defer meta.Close()
	defer os.Remove(meta.Name())

	// add any custom info to the metafile
	meta.WriteString(fmt.Sprintf(`{"engine_id": "%s"}`, engine.ID))

	// this is our predefined list of everything that gets archived as part of the
	// engine being published
	files := map[string][]string{
		"required": []string{"./bin", "./Enginefile", "./meta.json"},
		"optional": []string{"./lib", "./templates", "./files"},
	}

	// check to ensure no required files are missing
	for k, v := range files {
		if k == "required" {
			for _, f := range v {
				if _, err := os.Stat(f); err != nil {
					fmt.Printf(stylish.Error("required files missing", "Your Engine is missing one or more required files for publishing. Please read the following documentation to ensure all required files are included and try again.:\n\ndocs.nanobox.io/engines/project-creation/#example-engine-file-structure\n"))
					os.Exit(1)
				}
			}
		}
	}

	// create the temp engines folder for building the tarball
	tarPath := filepath.Join(config.EnginesDir, release.Name)
	if err := os.Mkdir(tarPath, 0755); err != nil {
		Config.Fatal("[commands/engine/publish] os.Create() failed", err.Error())
	}

	// remove tarDir once published
	defer func() {
		if err := os.RemoveAll(tarPath); err != nil {
			os.Stderr.WriteString(stylish.ErrBullet("Failed to remove '%v'...", tarPath))
		}
	}()

	// parse the ./Enginefile again to get the overlays
	if err := Config.ParseConfig("./Enginefile", opts); err != nil {
		fmt.Printf("Nanobox failed to parse your Enginefile. Please ensure it is valid YAML and try again.\n")
		os.Exit(1)
	}

	// iterate through each overlay fetching it and untaring to the tar path
	for _, overlay := range opts.Overlays {
		engineutil.GetOverlay(overlay, tarPath)
	}

	// range over each file from each file type, building the final list of files
	// to be tarballed
	for _, v := range files {
		for _, f := range v {

			// not handling error here because an error simply means the file doesn't
			// exist and therefor wont be copied to the final tarball
			fileutil.Copy(f, tarPath)
		}
	}

	// create an empty buffer for writing the file contents to for the subsequent
	// upload
	archive := bytes.NewBuffer(nil)

	//
	h := md5.New()

	//
	if err := fileutil.Tar(tarPath, archive, h); err != nil {
		Config.Fatal("[commands/engine/publish] file.Tar() failed", err.Error())
	}

	// add the checksum for the new release once its finished being archived
	release.Checksum = fmt.Sprintf("%x", h.Sum(nil))

	//
	// attempt to upload the release to S3
	fmt.Printf(stylish.Bullet("Uploading release to s3..."))

	v := url.Values{}
	v.Add("user_slug", api.UserSlug)
	v.Add("auth_token", api.AuthToken)
	v.Add("version", release.Version)

	//
	s3url, err := s3util.RequestURL(fmt.Sprintf("http://api.nanobox.io/v1/engines/%v/request_upload?%v", release.Name, v.Encode()))
	if err != nil {
		Config.Fatal("[commands/publish] s3.RequestURL() failed", err.Error())
	}

	//
	if err := s3util.Upload(s3url, archive); err != nil {
		Config.Fatal("[commands/publish] s3.Upload() failed", err.Error())
	}

	//
	// if the release uploaded successfully to s3, created one on odin
	fmt.Printf(stylish.Bullet("Uploading release to nanobox.io"))
	if _, err := api.CreateEngineRelease(release.Name, release); err != nil {
		fmt.Printf(stylish.ErrBullet("Unable to publish release (%v).", err))
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package common

import (
	gossh "code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/communicator/ssh"
	"github.com/mitchellh/packer/packer"
	"log"
	"strings"
	"time"
)

// StepConnectSSH is a multistep Step implementation that waits for SSH
// to become available. It gets the connection information from a single
// configuration when creating the step.
//
// Uses:
//   ui packer.Ui
//
// Produces:
//   communicator packer.Communicator
type StepConnectSSH struct {
	// SSHAddress is a function that returns the TCP address to connect to
	// for SSH. This is a function so that you can query information
	// if necessary for this address.
	SSHAddress func(multistep.StateBag) (string, error)

	// SSHConfig is a function that returns the proper client configuration
	// for SSH access.
	SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)

	// SSHWaitTimeout is the total timeout to wait for SSH to become available.
	SSHWaitTimeout time.Duration

	// NoPty, if true, will not request a Pty from the remote end.
	NoPty bool

	comm packer.Communicator
}

func (s *StepConnectSSH) Run(state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)

	var comm packer.Communicator
	var err error

	cancel := make(chan struct{})
	waitDone := make(chan bool, 1)
	go func() {
		ui.Say("Waiting for SSH to become available...")
		comm, err = s.waitForSSH(state, cancel)
		waitDone <- true
	}()

	log.Printf("Waiting for SSH, up to timeout: %s", s.SSHWaitTimeout)
	timeout := time.After(s.SSHWaitTimeout)
WaitLoop:
	for {
		// Wait for either SSH to become available, a timeout to occur,
		// or an interrupt to come through.
		select {
		case <-waitDone:
			if err != nil {
				ui.Error(fmt.Sprintf("Error waiting for SSH: %s", err))
				return multistep.ActionHalt
			}

			ui.Say("Connected to SSH!")
			s.comm = comm
			state.Put("communicator", comm)
			break WaitLoop
		case <-timeout:
			ui.Error("Timeout waiting for SSH.")
			close(cancel)
			return multistep.ActionHalt
		case <-time.After(1 * time.Second):
			if _, ok := state.GetOk(multistep.StateCancelled); ok {
				// The step sequence was cancelled, so cancel waiting for SSH
				// and just start the halting process.
				close(cancel)
				log.Println("Interrupt detected, quitting waiting for SSH.")
				return multistep.ActionHalt
			}
		}
	}

	return multistep.ActionContinue
}

func (s *StepConnectSSH) Cleanup(multistep.StateBag) {
}

func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
	handshakeAttempts := 0

	var comm packer.Communicator
	for {
		select {
		case <-cancel:
			log.Println("SSH wait cancelled. Exiting loop.")
			return nil, errors.New("SSH wait cancelled")
		case <-time.After(5 * time.Second):
		}

		// First we request the TCP connection information
		address, err := s.SSHAddress(state)
		if err != nil {
			log.Printf("Error getting SSH address: %s", err)
			continue
		}

		// Retrieve the SSH configuration
		sshConfig, err := s.SSHConfig(state)
		if err != nil {
			log.Printf("Error getting SSH config: %s", err)
			continue
		}

		// Attempt to connect to SSH port
		connFunc := ssh.ConnectFunc("tcp", address)
		nc, err := connFunc()
		if err != nil {
			log.Printf("TCP connection to SSH ip/port failed: %s", err)
			continue
		}
		nc.Close()

		// Then we attempt to connect via SSH
		config := &ssh.Config{
			Connection: connFunc,
			SSHConfig:  sshConfig,
			NoPty:      s.NoPty,
		}

		log.Println("Attempting SSH connection...")
		comm, err = ssh.New(config)
		if err != nil {
			log.Printf("SSH handshake err: %s", err)

			// Only count this as an attempt if we were able to attempt
			// to authenticate. Note this is very brittle since it depends
			// on the string of the error... but I don't see any other way.
			if strings.Contains(err.Error(), "authenticate") {
				log.Printf("Detected authentication error. Increasing handshake attempts.")
				handshakeAttempts += 1
			}

			if handshakeAttempts < 10 {
				// Try to connect via SSH a handful of times
				continue
			}

			return nil, err
		}

		break
	}

	return comm, nil
}
<commit_msg>An SSH timeout should be treated as an error<commit_after>package common

import (
	gossh "code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/communicator/ssh"
	"github.com/mitchellh/packer/packer"
	"log"
	"strings"
	"time"
)

// StepConnectSSH is a multistep Step implementation that waits for SSH
// to become available. It gets the connection information from a single
// configuration when creating the step.
//
// Uses:
//   ui packer.Ui
//
// Produces:
//   communicator packer.Communicator
type StepConnectSSH struct {
	// SSHAddress is a function that returns the TCP address to connect to
	// for SSH. This is a function so that you can query information
	// if necessary for this address.
	SSHAddress func(multistep.StateBag) (string, error)

	// SSHConfig is a function that returns the proper client configuration
	// for SSH access.
	SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)

	// SSHWaitTimeout is the total timeout to wait for SSH to become available.
	SSHWaitTimeout time.Duration

	// NoPty, if true, will not request a Pty from the remote end.
	NoPty bool

	comm packer.Communicator
}

func (s *StepConnectSSH) Run(state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)

	var comm packer.Communicator
	var err error

	cancel := make(chan struct{})
	waitDone := make(chan bool, 1)
	go func() {
		ui.Say("Waiting for SSH to become available...")
		comm, err = s.waitForSSH(state, cancel)
		waitDone <- true
	}()

	log.Printf("Waiting for SSH, up to timeout: %s", s.SSHWaitTimeout)
	timeout := time.After(s.SSHWaitTimeout)
WaitLoop:
	for {
		// Wait for either SSH to become available, a timeout to occur,
		// or an interrupt to come through.
		select {
		case <-waitDone:
			if err != nil {
				ui.Error(fmt.Sprintf("Error waiting for SSH: %s", err))
				return multistep.ActionHalt
			}

			ui.Say("Connected to SSH!")
			s.comm = comm
			state.Put("communicator", comm)
			break WaitLoop
		case <-timeout:
			err := fmt.Errorf("Timeout waiting for SSH.")
			state.Put("error", err)
			ui.Error(err.Error())
			close(cancel)
			return multistep.ActionHalt
		case <-time.After(1 * time.Second):
			if _, ok := state.GetOk(multistep.StateCancelled); ok {
				// The step sequence was cancelled, so cancel waiting for SSH
				// and just start the halting process.
				close(cancel)
				log.Println("Interrupt detected, quitting waiting for SSH.")
				return multistep.ActionHalt
			}
		}
	}

	return multistep.ActionContinue
}

func (s *StepConnectSSH) Cleanup(multistep.StateBag) {
}

func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
	handshakeAttempts := 0

	var comm packer.Communicator
	for {
		select {
		case <-cancel:
			log.Println("SSH wait cancelled. Exiting loop.")
			return nil, errors.New("SSH wait cancelled")
		case <-time.After(5 * time.Second):
		}

		// First we request the TCP connection information
		address, err := s.SSHAddress(state)
		if err != nil {
			log.Printf("Error getting SSH address: %s", err)
			continue
		}

		// Retrieve the SSH configuration
		sshConfig, err := s.SSHConfig(state)
		if err != nil {
			log.Printf("Error getting SSH config: %s", err)
			continue
		}

		// Attempt to connect to SSH port
		connFunc := ssh.ConnectFunc("tcp", address)
		nc, err := connFunc()
		if err != nil {
			log.Printf("TCP connection to SSH ip/port failed: %s", err)
			continue
		}
		nc.Close()

		// Then we attempt to connect via SSH
		config := &ssh.Config{
			Connection: connFunc,
			SSHConfig:  sshConfig,
			NoPty:      s.NoPty,
		}

		log.Println("Attempting SSH connection...")
		comm, err = ssh.New(config)
		if err != nil {
			log.Printf("SSH handshake err: %s", err)

			// Only count this as an attempt if we were able to attempt
			// to authenticate. Note this is very brittle since it depends
			// on the string of the error... but I don't see any other way.
			if strings.Contains(err.Error(), "authenticate") {
				log.Printf("Detected authentication error. Increasing handshake attempts.")
				handshakeAttempts += 1
			}

			if handshakeAttempts < 10 {
				// Try to connect via SSH a handful of times
				continue
			}

			return nil, err
		}

		break
	}

	return comm, nil
}
<|endoftext|>


<commit_before><commit_msg>some manual fixes<commit_after><|endoftext|>


<commit_before>package sqlx

import (
	"bytes"
	"strconv"
)

// Bindvar types supported by Rebind, BindMap and BindStruct.
const (
	UNKNOWN = iota
	QUESTION
	DOLLAR
	NAMED
)

// BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int {
	switch driverName {
	case "postgres", "pgx":
		return DOLLAR
	case "mysql":
		return QUESTION
	case "sqlite":
		return QUESTION
	case "oci8":
		return NAMED
	}
	return UNKNOWN
}

// FIXME: this should be able to be tolerant of escaped ?'s in queries without
// losing much speed, and should be to avoid confusion.

// FIXME: this is now produces the wrong results for oracle's NAMED bindtype

// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
func Rebind(bindType int, query string) string {
	if bindType != DOLLAR {
		return query
	}

	qb := []byte(query)
	// Add space enough for 10 params before we have to allocate
	rqb := make([]byte, 0, len(qb)+10)
	j := 1
	for _, b := range qb {
		if b == '?' {
			rqb = append(rqb, '$')
			for _, b := range strconv.Itoa(j) {
				rqb = append(rqb, byte(b))
			}
			j++
		} else {
			rqb = append(rqb, b)
		}
	}
	return string(rqb)
}

// Experimental implementation of Rebind which uses a bytes.Buffer.  The code is
// much simpler and should be more resistant to odd unicode, but it is twice as
// slow.  Kept here for benchmarking purposes and to possibly replace Rebind if
// problems arise with its somewhat naive handling of unicode.

func rebindBuff(bindType int, query string) string {
	if bindType != DOLLAR {
		return query
	}

	b := make([]byte, 0, len(query))
	rqb := bytes.NewBuffer(b)
	j := 1
	for _, r := range query {
		if r == '?' {
			rqb.WriteRune('$')
			rqb.WriteString(strconv.Itoa(j))
			j++
		} else {
			rqb.WriteRune(r)
		}
	}

	return rqb.String()
}
<commit_msg>identify sqlite3 driver properly in bind.go<commit_after>package sqlx

import (
	"bytes"
	"strconv"
)

// Bindvar types supported by Rebind, BindMap and BindStruct.
const (
	UNKNOWN = iota
	QUESTION
	DOLLAR
	NAMED
)

// BindType returns the bindtype for a given database given a drivername.
func BindType(driverName string) int {
	switch driverName {
	case "postgres", "pgx":
		return DOLLAR
	case "mysql":
		return QUESTION
	case "sqlite3":
		return QUESTION
	case "oci8":
		return NAMED
	}
	return UNKNOWN
}

// FIXME: this should be able to be tolerant of escaped ?'s in queries without
// losing much speed, and should be to avoid confusion.

// FIXME: this is now produces the wrong results for oracle's NAMED bindtype

// Rebind a query from the default bindtype (QUESTION) to the target bindtype.
func Rebind(bindType int, query string) string {
	if bindType != DOLLAR {
		return query
	}

	qb := []byte(query)
	// Add space enough for 10 params before we have to allocate
	rqb := make([]byte, 0, len(qb)+10)
	j := 1
	for _, b := range qb {
		if b == '?' {
			rqb = append(rqb, '$')
			for _, b := range strconv.Itoa(j) {
				rqb = append(rqb, byte(b))
			}
			j++
		} else {
			rqb = append(rqb, b)
		}
	}
	return string(rqb)
}

// Experimental implementation of Rebind which uses a bytes.Buffer.  The code is
// much simpler and should be more resistant to odd unicode, but it is twice as
// slow.  Kept here for benchmarking purposes and to possibly replace Rebind if
// problems arise with its somewhat naive handling of unicode.

func rebindBuff(bindType int, query string) string {
	if bindType != DOLLAR {
		return query
	}

	b := make([]byte, 0, len(query))
	rqb := bytes.NewBuffer(b)
	j := 1
	for _, r := range query {
		if r == '?' {
			rqb.WriteRune('$')
			rqb.WriteString(strconv.Itoa(j))
			j++
		} else {
			rqb.WriteRune(r)
		}
	}

	return rqb.String()
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder_test

import (
	"database/sql"
	"github.com/issue9/orm/v2/internal/sqltest"

	"testing"

	"github.com/issue9/assert"
	"github.com/issue9/orm/v2"
	"github.com/issue9/orm/v2/dialect"
	"github.com/issue9/orm/v2/sqlbuilder"
)

var (
	_ sqlbuilder.SQLer       = &sqlbuilder.SelectStmt{}
	_ sqlbuilder.WhereStmter = &sqlbuilder.SelectStmt{}
)

func TestSelect_Query(t *testing.T) {
	a := assert.New(t)
	db := initDB(a)
	defer clearDB(a, db)

	sql := sqlbuilder.Select(db, db.Dialect()).Select("*").
		From("#user").
		Where("id<?", 5).
		Desc("id")

	id, err := sql.QueryInt("id")
	a.NotError(err).
		Equal(id, 4)

	name, err := sql.QueryString("name")
	a.NotError(err).
		Equal(name, "4")

	obj := &user{}
	size, err := sql.QueryObject(true, obj)
	a.NotError(err).Equal(1, size)
	a.Equal(obj.ID, 4)

	cnt, err := sql.Count("count(*) AS cnt").QueryInt("cnt")
	a.NotError(err).
		Equal(cnt, 4)
}

func TestSelect_sqlite3(t *testing.T) {
	a := assert.New(t)
	e, err := orm.NewDB("sqlite3", "./test.db", "test_", dialect.Sqlite3())
	a.NotError(err)
	s := sqlbuilder.Select(e, e.Dialect()).Select("c1", "column2 as c2", "c3").
		From("table").
		And("c1=?", 1).
		Or("c2=@c2", sql.Named("c2", 2)).
		Limit(10, 0).
		Desc("c1")
	a.NotNil(s)
	query, args, err := s.SQL()
	a.NotError(err)
	a.Equal(args, []interface{}{1, sql.Named("c2", 2), 10, 0})
	sqltest.Equal(a, query, "select c1,column2 as c2,c3 from table where c1=? or c2=@c2 order by c1 desc limit ? offset ?")

	// count
	s.Count("count(*) as cnt")
	query, args, err = s.SQL()
	a.NotError(err)
	a.Equal(args, []interface{}{1, sql.Named("c2", 2)})
	sqltest.Equal(a, query, "select count(*) as cnt from table where c1=? or c2=@c2")

	// reset
	s.Reset()
	query, args, err = s.SQL()
	a.Error(err)

	s.Distinct().
		Select("c1,c2,c3").
		Join("left", "users as u", "a.id=u.id").
		Where("id=?", 5).
		Asc("id").
		From("table1 as t")
	query, args, err = s.SQL()
	a.NotError(err)
	a.Equal(args, []interface{}{5})
	sqltest.Equal(a, query, "select distinct c1,c2,c3 from table1 as t left join users as u on a.id=u.id where id=? order by id asc")

	// s.reset，没有  where
	s.Reset()
	s.Select("c1,c2").From("#tb1")
	query, args, err = s.SQL()
	a.NotError(err).Empty(args)
	sqltest.Equal(a, query, "select c1,c2 from #tb1")
}
<commit_msg>sql select quiery string<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder_test

import (
	"database/sql"
	"testing"

	"github.com/issue9/assert"
	"github.com/issue9/orm/v2"
	"github.com/issue9/orm/v2/dialect"
	"github.com/issue9/orm/v2/internal/sqltest"
	"github.com/issue9/orm/v2/sqlbuilder"
)

var (
	_ sqlbuilder.SQLer       = &sqlbuilder.SelectStmt{}
	_ sqlbuilder.WhereStmter = &sqlbuilder.SelectStmt{}
)

func TestSelect_Query(t *testing.T) {
	a := assert.New(t)
	db := initDB(a)
	defer clearDB(a, db)

	sql := sqlbuilder.Select(db, db.Dialect()).Select("*").
		From("#user").
		Where("id<?", 5).
		Desc("id")

	id, err := sql.QueryInt("id")
	a.NotError(err).
		Equal(id, 4)

	name, err := sql.QueryString("name")
	a.NotError(err).
		Equal(name, "4")

	obj := &user{}
	size, err := sql.QueryObject(true, obj)
	a.NotError(err).Equal(1, size)
	a.Equal(obj.ID, 4)

	cnt, err := sql.Count("count(*) AS cnt").QueryInt("cnt")
	a.NotError(err).
		Equal(cnt, 4)
}

func TestSelect_sqlite3(t *testing.T) {
	a := assert.New(t)
	e, err := orm.NewDB("sqlite3", "./test.db", "test_", dialect.Sqlite3())
	a.NotError(err)
	s := sqlbuilder.Select(e, e.Dialect()).Select("c1", "column2 as c2", "c3").
		From("table").
		And("c1=?", 1).
		Or("c2=@c2", sql.Named("c2", 2)).
		Limit(10, 0).
		Desc("c1")
	a.NotNil(s)
	query, args, err := s.SQL()
	a.NotError(err)
	a.Equal(args, []interface{}{1, sql.Named("c2", 2), 10, 0})
	sqltest.Equal(a, query, "select c1,column2 as c2,c3 from table where c1=? or c2=@c2 order by c1 desc limit ? offset ?")

	// count
	s.Count("count(*) as cnt")
	query, args, err = s.SQL()
	a.NotError(err)
	a.Equal(args, []interface{}{1, sql.Named("c2", 2)})
	sqltest.Equal(a, query, "select count(*) as cnt from table where c1=? or c2=@c2")

	// reset
	s.Reset()
	query, args, err = s.SQL()
	a.Error(err)

	s.Distinct().
		Select("c1,c2,c3").
		Join("left", "users as u", "a.id=u.id").
		Where("id=?", 5).
		Asc("id").
		From("table1 as t")
	query, args, err = s.SQL()
	a.NotError(err)
	a.Equal(args, []interface{}{5})
	sqltest.Equal(a, query, "select distinct c1,c2,c3 from table1 as t left join users as u on a.id=u.id where id=? order by id asc")

	// s.reset，没有  where
	s.Reset()
	s.Select("c1,c2").From("#tb1")
	query, args, err = s.SQL()
	a.NotError(err).Empty(args)
	sqltest.Equal(a, query, "select c1,c2 from #tb1")
}
<|endoftext|>