<commit_before>package collectors

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/prometheus/client_golang/prometheus"
)

type OsdCollector struct {
	conn Conn

	CrushWeight *prometheus.GaugeVec

	Depth *prometheus.GaugeVec

	Reweight *prometheus.GaugeVec

	KB *prometheus.GaugeVec

	UsedKB *prometheus.GaugeVec

	AvailKB *prometheus.GaugeVec

	Utilization *prometheus.GaugeVec

	Pgs *prometheus.GaugeVec

	CommitLatency *prometheus.GaugeVec

	ApplyLatency *prometheus.GaugeVec

	OsdIn *prometheus.GaugeVec

	OsdUp *prometheus.GaugeVec

	TotalKB prometheus.Gauge

	TotalUsedKB prometheus.Gauge

	TotalAvailKB prometheus.Gauge

	AverageUtil prometheus.Gauge
}

func NewOsdCollector(conn Conn) *OsdCollector {
	return &OsdCollector{
		conn: conn,

		CrushWeight: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_crush_weight",
				Help:      "OSD Crush Weight",
			},
			[]string{"osd"},
		),

		Depth: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_depth",
				Help:      "OSD Depth",
			},
			[]string{"osd"},
		),

		Reweight: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_reweight",
				Help:      "OSD Reweight",
			},
			[]string{"osd"},
		),

		KB: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_kb",
				Help:      "OSD Total KB",
			},
			[]string{"osd"},
		),

		UsedKB: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_kb_used",
				Help:      "OSD Used Storage in KB",
			},
			[]string{"osd"},
		),

		AvailKB: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_kb_avail",
				Help:      "OSD Available Storage in KB",
			},
			[]string{"osd"},
		),

		Utilization: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_utilization",
				Help:      "OSD Utilization",
			},
			[]string{"osd"},
		),

		Pgs: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_pgs",
				Help:      "OSD Placement Group Count",
			},
			[]string{"osd"},
		),

		TotalKB: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_total_kb",
				Help:      "OSD Total Storage KB",
			},
		),
		TotalUsedKB: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_total_kb_used",
				Help:      "OSD Total Used Storage KB",
			},
		),

		TotalAvailKB: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_total_kb_avail",
				Help:      "OSD Total Available Storage KB ",
			},
		),

		AverageUtil: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_average_utilization",
				Help:      "OSD Average Utilization",
			},
		),

		CommitLatency: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_perf_commit_latency_ms",
				Help:      "OSD Perf Commit Latency",
			},
			[]string{"osd"},
		),

		ApplyLatency: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_perf_apply_latency_ms",
				Help:      "OSD Perf Apply Latency",
			},
			[]string{"osd"},
		),

		OsdIn: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_in",
				Help:      "OSD In Status",
			},
			[]string{"osd"},
		),

		OsdUp: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_up",
				Help:      "OSD Up Status",
			},
			[]string{"osd"},
		),
	}
}

func (o *OsdCollector) collectorList() []prometheus.Collector {
	return []prometheus.Collector{
		o.CrushWeight,
		o.Depth,
		o.Reweight,
		o.KB,
		o.UsedKB,
		o.AvailKB,
		o.Utilization,
		o.Pgs,
		o.TotalKB,
		o.TotalUsedKB,
		o.TotalAvailKB,
		o.AverageUtil,
		o.CommitLatency,
		o.ApplyLatency,
		o.OsdIn,
		o.OsdUp,
	}
}

type cephOsdDf struct {
	OsdNodes []struct {
		Name        string      `json:"name"`
		CrushWeight json.Number `json:"crush_weight"`
		Depth       json.Number `json:"depth"`
		Reweight    json.Number `json:"reweight"`
		KB          json.Number `json:"kb"`
		UsedKB      json.Number `json:"kb_used"`
		AvailKB     json.Number `json:"kb_avail"`
		Utilization json.Number `json:"utilization"`
		Pgs         json.Number `json:"pgs"`
	} `json:"nodes"`

	Summary struct {
		TotalKB      json.Number `json:"total_kb"`
		TotalUsedKB  json.Number `json:"total_kb_used"`
		TotalAvailKB json.Number `json:"total_kb_avail"`
		AverageUtil  json.Number `json:"average_utilization"`
	} `json:"summary"`
}

type cephPerfStat struct {
	PerfInfo []struct {
		Id    json.Number `json:"id"`
		Stats struct {
			CommitLatency json.Number `json:"commit_latency_ms"`
			ApplyLatency  json.Number `json:"apply_latency_ms"`
		} `json:"perf_stats"`
	} `json:"osd_perf_infos"`
}

type cephOsdDump struct {
	Osds []struct {
		Osd json.Number `json:"osd"`
		Up  json.Number `json:"up"`
		In  json.Number `json:"in"`
	} `json:"osds"`
}

func (o *OsdCollector) collect() error {
	cmd := o.cephOSDDfCommand()

	buf, _, err := o.conn.MonCommand(cmd)
	if err != nil {
		log.Println("[ERROR] Unable to collect data from ceph osd df", err)
		return err
	}

	osdDf := &cephOsdDf{}
	if err := json.Unmarshal(buf, osdDf); err != nil {
		return err
	}

	for _, node := range osdDf.OsdNodes {

		crushWeight, err := node.CrushWeight.Float64()
		if err != nil {
			return err
		}

		o.CrushWeight.WithLabelValues(node.Name).Set(crushWeight)

		depth, err := node.Depth.Float64()
		if err != nil {

			return err
		}

		o.Depth.WithLabelValues(node.Name).Set(depth)

		reweight, err := node.Reweight.Float64()
		if err != nil {
			return err
		}

		o.Reweight.WithLabelValues(node.Name).Set(reweight)

		kb, err := node.KB.Float64()
		if err != nil {
			return nil
		}

		o.KB.WithLabelValues(node.Name).Set(kb)

		usedKb, err := node.UsedKB.Float64()
		if err != nil {
			return err
		}

		o.UsedKB.WithLabelValues(node.Name).Set(usedKb)

		availKb, err := node.AvailKB.Float64()
		if err != nil {
			return err
		}

		o.AvailKB.WithLabelValues(node.Name).Set(availKb)

		util, err := node.Utilization.Float64()
		if err != nil {
			return err
		}

		o.Utilization.WithLabelValues(node.Name).Set(util)

		pgs, err := node.Pgs.Float64()
		if err != nil {
			return err
		}

		o.Pgs.WithLabelValues(node.Name).Set(pgs)

	}

	totalKb, err := osdDf.Summary.TotalKB.Float64()
	if err != nil {
		return err
	}

	o.TotalKB.Set(totalKb)

	totalUsedKb, err := osdDf.Summary.TotalUsedKB.Float64()
	if err != nil {
		return err
	}

	o.TotalUsedKB.Set(totalUsedKb)

	totalAvailKb, err := osdDf.Summary.TotalAvailKB.Float64()
	if err != nil {
		return err
	}

	o.TotalAvailKB.Set(totalAvailKb)

	averageUtil, err := osdDf.Summary.AverageUtil.Float64()
	if err != nil {
		return err
	}

	o.AverageUtil.Set(averageUtil)

	return nil

}

func (o *OsdCollector) collectOsdPerf() error {
	osdPerfCmd := o.cephOSDPerfCommand()
	buf, _, err := o.conn.MonCommand(osdPerfCmd)
	if err != nil {
		log.Println("[ERROR] Unable to collect data from ceph osd perf", err)
		return err
	}

	osdPerf := &cephPerfStat{}
	if err := json.Unmarshal(buf, osdPerf); err != nil {
		return err
	}

	for _, perfStat := range osdPerf.PerfInfo {
		osdId, err := perfStat.Id.Int64()
		if err != nil {
			return err
		}
		osdName := fmt.Sprintf("osd.%v", osdId)

		commitLatency, err := perfStat.Stats.CommitLatency.Float64()
		if err != nil {
			return err
		}
		o.CommitLatency.WithLabelValues(osdName).Set(commitLatency)

		applyLatency, err := perfStat.Stats.ApplyLatency.Float64()
		if err != nil {
			return err
		}
		o.ApplyLatency.WithLabelValues(osdName).Set(applyLatency)
	}

	return nil
}

func (o *OsdCollector) collectOsdDump() error {
	osdDumpCmd := o.cephOsdDump()
	buff, _, err := o.conn.MonCommand(osdDumpCmd)
	if err != nil {
		log.Println("[ERROR] Unable to collect data from ceph osd dump", err)
		return err
	}

	osdDump := &cephOsdDump{}
	if err := json.Unmarshal(buff, osdDump); err != nil {
		return err
	}

	for _, dumpInfo := range osdDump.Osds {
		osdId, err := dumpInfo.Osd.Int64()
		if err != nil {
			return err
		}
		osdName := fmt.Sprintf("osd.%v", osdId)

		in, err := dumpInfo.In.Float64()
		if err != nil {
			return err
		}

		o.OsdIn.WithLabelValues(osdName).Set(in)

		up, err := dumpInfo.Up.Float64()
		if err != nil {
			return err
		}

		o.OsdUp.WithLabelValues(osdName).Set(up)
	}

	return nil

}

func (o *OsdCollector) cephOsdDump() []byte {
	cmd, err := json.Marshal(map[string]interface{}{
		"prefix": "osd dump",
		"format": "json",
	})
	if err != nil {
		panic(err)
	}
	return cmd
}

func (o *OsdCollector) cephOSDDfCommand() []byte {
	cmd, err := json.Marshal(map[string]interface{}{
		"prefix": "osd df",
		"format": "json",
	})
	if err != nil {
		panic(err)
	}
	return cmd
}

func (o *OsdCollector) cephOSDPerfCommand() []byte {
	cmd, err := json.Marshal(map[string]interface{}{
		"prefix": "osd perf",
		"format": "json",
	})
	if err != nil {
		panic(err)
	}
	return cmd
}

func (o *OsdCollector) Describe(ch chan<- *prometheus.Desc) {
	for _, metric := range o.collectorList() {
		metric.Describe(ch)
	}

}

func (o *OsdCollector) Collect(ch chan<- prometheus.Metric) {

	if err := o.collectOsdPerf(); err != nil {
		log.Println("failed collecting cluster osd perf stats:", err)
	}

	if err := o.collectOsdDump(); err != nil {
		log.Println("failed collecting cluster osd dump", err)
	}

	if err := o.collect(); err != nil {
		log.Println("failed collecting osd metrics:", err)
	}

	for _, metric := range o.collectorList() {
		metric.Collect(ch)
	}

}
<commit_msg>continue to next osd. pgs is not avaailable in ceph hammer.<commit_after>package collectors

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/prometheus/client_golang/prometheus"
)

type OsdCollector struct {
	conn Conn

	CrushWeight *prometheus.GaugeVec

	Depth *prometheus.GaugeVec

	Reweight *prometheus.GaugeVec

	KB *prometheus.GaugeVec

	UsedKB *prometheus.GaugeVec

	AvailKB *prometheus.GaugeVec

	Utilization *prometheus.GaugeVec

	Pgs *prometheus.GaugeVec

	CommitLatency *prometheus.GaugeVec

	ApplyLatency *prometheus.GaugeVec

	OsdIn *prometheus.GaugeVec

	OsdUp *prometheus.GaugeVec

	TotalKB prometheus.Gauge

	TotalUsedKB prometheus.Gauge

	TotalAvailKB prometheus.Gauge

	AverageUtil prometheus.Gauge
}

func NewOsdCollector(conn Conn) *OsdCollector {
	return &OsdCollector{
		conn: conn,

		CrushWeight: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_crush_weight",
				Help:      "OSD Crush Weight",
			},
			[]string{"osd"},
		),

		Depth: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_depth",
				Help:      "OSD Depth",
			},
			[]string{"osd"},
		),

		Reweight: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_reweight",
				Help:      "OSD Reweight",
			},
			[]string{"osd"},
		),

		KB: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_kb",
				Help:      "OSD Total KB",
			},
			[]string{"osd"},
		),

		UsedKB: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_kb_used",
				Help:      "OSD Used Storage in KB",
			},
			[]string{"osd"},
		),

		AvailKB: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_kb_avail",
				Help:      "OSD Available Storage in KB",
			},
			[]string{"osd"},
		),

		Utilization: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_utilization",
				Help:      "OSD Utilization",
			},
			[]string{"osd"},
		),

		Pgs: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_pgs",
				Help:      "OSD Placement Group Count",
			},
			[]string{"osd"},
		),

		TotalKB: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_total_kb",
				Help:      "OSD Total Storage KB",
			},
		),
		TotalUsedKB: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_total_kb_used",
				Help:      "OSD Total Used Storage KB",
			},
		),

		TotalAvailKB: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_total_kb_avail",
				Help:      "OSD Total Available Storage KB ",
			},
		),

		AverageUtil: prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_average_utilization",
				Help:      "OSD Average Utilization",
			},
		),

		CommitLatency: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_perf_commit_latency_ms",
				Help:      "OSD Perf Commit Latency",
			},
			[]string{"osd"},
		),

		ApplyLatency: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_perf_apply_latency_ms",
				Help:      "OSD Perf Apply Latency",
			},
			[]string{"osd"},
		),

		OsdIn: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_in",
				Help:      "OSD In Status",
			},
			[]string{"osd"},
		),

		OsdUp: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: cephNamespace,
				Name:      "osd_up",
				Help:      "OSD Up Status",
			},
			[]string{"osd"},
		),
	}
}

func (o *OsdCollector) collectorList() []prometheus.Collector {
	return []prometheus.Collector{
		o.CrushWeight,
		o.Depth,
		o.Reweight,
		o.KB,
		o.UsedKB,
		o.AvailKB,
		o.Utilization,
		o.Pgs,
		o.TotalKB,
		o.TotalUsedKB,
		o.TotalAvailKB,
		o.AverageUtil,
		o.CommitLatency,
		o.ApplyLatency,
		o.OsdIn,
		o.OsdUp,
	}
}

type cephOsdDf struct {
	OsdNodes []struct {
		Name        string      `json:"name"`
		CrushWeight json.Number `json:"crush_weight"`
		Depth       json.Number `json:"depth"`
		Reweight    json.Number `json:"reweight"`
		KB          json.Number `json:"kb"`
		UsedKB      json.Number `json:"kb_used"`
		AvailKB     json.Number `json:"kb_avail"`
		Utilization json.Number `json:"utilization"`
		Pgs         json.Number `json:"pgs"`
	} `json:"nodes"`

	Summary struct {
		TotalKB      json.Number `json:"total_kb"`
		TotalUsedKB  json.Number `json:"total_kb_used"`
		TotalAvailKB json.Number `json:"total_kb_avail"`
		AverageUtil  json.Number `json:"average_utilization"`
	} `json:"summary"`
}

type cephPerfStat struct {
	PerfInfo []struct {
		Id    json.Number `json:"id"`
		Stats struct {
			CommitLatency json.Number `json:"commit_latency_ms"`
			ApplyLatency  json.Number `json:"apply_latency_ms"`
		} `json:"perf_stats"`
	} `json:"osd_perf_infos"`
}

type cephOsdDump struct {
	Osds []struct {
		Osd json.Number `json:"osd"`
		Up  json.Number `json:"up"`
		In  json.Number `json:"in"`
	} `json:"osds"`
}

func (o *OsdCollector) collect() error {
	cmd := o.cephOSDDfCommand()

	buf, _, err := o.conn.MonCommand(cmd)
	if err != nil {
		log.Println("[ERROR] Unable to collect data from ceph osd df", err)
		return err
	}

	osdDf := &cephOsdDf{}
	if err := json.Unmarshal(buf, osdDf); err != nil {
		return err
	}

	for _, node := range osdDf.OsdNodes {

		crushWeight, err := node.CrushWeight.Float64()
		if err != nil {
			return err
		}

		o.CrushWeight.WithLabelValues(node.Name).Set(crushWeight)

		depth, err := node.Depth.Float64()
		if err != nil {

			return err
		}

		o.Depth.WithLabelValues(node.Name).Set(depth)

		reweight, err := node.Reweight.Float64()
		if err != nil {
			return err
		}

		o.Reweight.WithLabelValues(node.Name).Set(reweight)

		kb, err := node.KB.Float64()
		if err != nil {
			return nil
		}

		o.KB.WithLabelValues(node.Name).Set(kb)

		usedKb, err := node.UsedKB.Float64()
		if err != nil {
			return err
		}

		o.UsedKB.WithLabelValues(node.Name).Set(usedKb)

		availKb, err := node.AvailKB.Float64()
		if err != nil {
			return err
		}

		o.AvailKB.WithLabelValues(node.Name).Set(availKb)

		util, err := node.Utilization.Float64()
		if err != nil {
			return err
		}

		o.Utilization.WithLabelValues(node.Name).Set(util)

		pgs, err := node.Pgs.Float64()
		if err != nil {
			continue
		}

		o.Pgs.WithLabelValues(node.Name).Set(pgs)

	}

	totalKb, err := osdDf.Summary.TotalKB.Float64()
	if err != nil {
		return err
	}

	o.TotalKB.Set(totalKb)

	totalUsedKb, err := osdDf.Summary.TotalUsedKB.Float64()
	if err != nil {
		return err
	}

	o.TotalUsedKB.Set(totalUsedKb)

	totalAvailKb, err := osdDf.Summary.TotalAvailKB.Float64()
	if err != nil {
		return err
	}

	o.TotalAvailKB.Set(totalAvailKb)

	averageUtil, err := osdDf.Summary.AverageUtil.Float64()
	if err != nil {
		return err
	}

	o.AverageUtil.Set(averageUtil)

	return nil

}

func (o *OsdCollector) collectOsdPerf() error {
	osdPerfCmd := o.cephOSDPerfCommand()
	buf, _, err := o.conn.MonCommand(osdPerfCmd)
	if err != nil {
		log.Println("[ERROR] Unable to collect data from ceph osd perf", err)
		return err
	}

	osdPerf := &cephPerfStat{}
	if err := json.Unmarshal(buf, osdPerf); err != nil {
		return err
	}

	for _, perfStat := range osdPerf.PerfInfo {
		osdId, err := perfStat.Id.Int64()
		if err != nil {
			return err
		}
		osdName := fmt.Sprintf("osd.%v", osdId)

		commitLatency, err := perfStat.Stats.CommitLatency.Float64()
		if err != nil {
			return err
		}
		o.CommitLatency.WithLabelValues(osdName).Set(commitLatency)

		applyLatency, err := perfStat.Stats.ApplyLatency.Float64()
		if err != nil {
			return err
		}
		o.ApplyLatency.WithLabelValues(osdName).Set(applyLatency)
	}

	return nil
}

func (o *OsdCollector) collectOsdDump() error {
	osdDumpCmd := o.cephOsdDump()
	buff, _, err := o.conn.MonCommand(osdDumpCmd)
	if err != nil {
		log.Println("[ERROR] Unable to collect data from ceph osd dump", err)
		return err
	}

	osdDump := &cephOsdDump{}
	if err := json.Unmarshal(buff, osdDump); err != nil {
		return err
	}

	for _, dumpInfo := range osdDump.Osds {
		osdId, err := dumpInfo.Osd.Int64()
		if err != nil {
			return err
		}
		osdName := fmt.Sprintf("osd.%v", osdId)

		in, err := dumpInfo.In.Float64()
		if err != nil {
			return err
		}

		o.OsdIn.WithLabelValues(osdName).Set(in)

		up, err := dumpInfo.Up.Float64()
		if err != nil {
			return err
		}

		o.OsdUp.WithLabelValues(osdName).Set(up)
	}

	return nil

}

func (o *OsdCollector) cephOsdDump() []byte {
	cmd, err := json.Marshal(map[string]interface{}{
		"prefix": "osd dump",
		"format": "json",
	})
	if err != nil {
		panic(err)
	}
	return cmd
}

func (o *OsdCollector) cephOSDDfCommand() []byte {
	cmd, err := json.Marshal(map[string]interface{}{
		"prefix": "osd df",
		"format": "json",
	})
	if err != nil {
		panic(err)
	}
	return cmd
}

func (o *OsdCollector) cephOSDPerfCommand() []byte {
	cmd, err := json.Marshal(map[string]interface{}{
		"prefix": "osd perf",
		"format": "json",
	})
	if err != nil {
		panic(err)
	}
	return cmd
}

func (o *OsdCollector) Describe(ch chan<- *prometheus.Desc) {
	for _, metric := range o.collectorList() {
		metric.Describe(ch)
	}

}

func (o *OsdCollector) Collect(ch chan<- prometheus.Metric) {

	if err := o.collectOsdPerf(); err != nil {
		log.Println("failed collecting cluster osd perf stats:", err)
	}

	if err := o.collectOsdDump(); err != nil {
		log.Println("failed collecting cluster osd dump", err)
	}

	if err := o.collect(); err != nil {
		log.Println("failed collecting osd metrics:", err)
	}

	for _, metric := range o.collectorList() {
		metric.Collect(ch)
	}

}
<|endoftext|>


<commit_before>package driver

import (
	"io"
	"log"
	"net"
	"strings"

	"github.com/hashicorp/go-plugin"
	"github.com/hashicorp/logutils"
)

var HandshakeConfig = plugin.HandshakeConfig{
	ProtocolVersion:  1,
	MagicCookieKey:   "NOMAD_PLUGIN_MAGIC_COOKIE",
	MagicCookieValue: "e4327c2e01eabfd75a8a67adb114fb34a757d57eee7728d857a8cec6e91a7255",
}

func GetPluginMap(w io.Writer, logLevel string) map[string]plugin.Plugin {
	e := new(ExecutorPlugin)
	filter := &logutils.LevelFilter{
		Levels:   []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERR"},
		MinLevel: logutils.LogLevel(strings.ToUpper(logLevel)),
		Writer:   w,
	}

	e.logger = log.New(filter, "", log.LstdFlags|log.Lmicroseconds)

	return map[string]plugin.Plugin{
		"executor": e,
	}
}

// ExecutorReattachConfig is the config that we seralize and de-serialize and
// store in disk
type PluginReattachConfig struct {
	Pid      int
	AddrNet  string
	AddrName string
}

// PluginConfig returns a config from an ExecutorReattachConfig
func (c *PluginReattachConfig) PluginConfig() *plugin.ReattachConfig {
	var addr net.Addr
	switch c.AddrNet {
	case "unix", "unixgram", "unixpacket":
		addr, _ = net.ResolveUnixAddr(c.AddrNet, c.AddrName)
	case "tcp", "tcp4", "tcp6":
		addr, _ = net.ResolveTCPAddr(c.AddrNet, c.AddrName)
	}
	return &plugin.ReattachConfig{Pid: c.Pid, Addr: addr}
}

func NewPluginReattachConfig(c *plugin.ReattachConfig) *PluginReattachConfig {
	return &PluginReattachConfig{Pid: c.Pid, AddrNet: c.Addr.Network(), AddrName: c.Addr.String()}
}
<commit_msg>spelling: serialize<commit_after>package driver

import (
	"io"
	"log"
	"net"
	"strings"

	"github.com/hashicorp/go-plugin"
	"github.com/hashicorp/logutils"
)

var HandshakeConfig = plugin.HandshakeConfig{
	ProtocolVersion:  1,
	MagicCookieKey:   "NOMAD_PLUGIN_MAGIC_COOKIE",
	MagicCookieValue: "e4327c2e01eabfd75a8a67adb114fb34a757d57eee7728d857a8cec6e91a7255",
}

func GetPluginMap(w io.Writer, logLevel string) map[string]plugin.Plugin {
	e := new(ExecutorPlugin)
	filter := &logutils.LevelFilter{
		Levels:   []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERR"},
		MinLevel: logutils.LogLevel(strings.ToUpper(logLevel)),
		Writer:   w,
	}

	e.logger = log.New(filter, "", log.LstdFlags|log.Lmicroseconds)

	return map[string]plugin.Plugin{
		"executor": e,
	}
}

// ExecutorReattachConfig is the config that we serialize and de-serialize and
// store in disk
type PluginReattachConfig struct {
	Pid      int
	AddrNet  string
	AddrName string
}

// PluginConfig returns a config from an ExecutorReattachConfig
func (c *PluginReattachConfig) PluginConfig() *plugin.ReattachConfig {
	var addr net.Addr
	switch c.AddrNet {
	case "unix", "unixgram", "unixpacket":
		addr, _ = net.ResolveUnixAddr(c.AddrNet, c.AddrName)
	case "tcp", "tcp4", "tcp6":
		addr, _ = net.ResolveTCPAddr(c.AddrNet, c.AddrName)
	}
	return &plugin.ReattachConfig{Pid: c.Pid, Addr: addr}
}

func NewPluginReattachConfig(c *plugin.ReattachConfig) *PluginReattachConfig {
	return &PluginReattachConfig{Pid: c.Pid, AddrNet: c.Addr.Network(), AddrName: c.Addr.String()}
}
<|endoftext|>


<commit_before>package adsi

import (
	"encoding/hex"
	"fmt"
	"sync"
	"unsafe"

	"github.com/google/uuid"
	"github.com/scjalliance/comshim"
	"github.com/scjalliance/comutil"
	"gopkg.in/adsi.v0/api"
	"gopkg.in/adsi.v0/comiid"
)

// ADSI Objects of LDAP:  https://msdn.microsoft.com/library/aa772208
// ADSI Objects of WinNT: https://msdn.microsoft.com/library/aa772211

// Object provides access to Active Directory objects.
type Object struct {
	object
}

// NewObject returns an object that manages the given COM interface.
func NewObject(iface *api.IADs) *Object {
	comshim.Add(1)
	return &Object{object{iface: iface}}
}

type object struct {
	m     sync.RWMutex
	iface *api.IADs
}

func (o *object) closed() bool {
	return (o.iface == nil)
}

// Close will release resources consumed by the object. It should be
// called when the object is no longer needed.
func (o *object) Close() {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return
	}
	defer comshim.Done()
	o.iface.Release() // FIXME: What happens if release returns an error?
	o.iface = nil
}

// Name retrieves the name of the object.
func (o *object) Name() (name string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	name, err = o.iface.Name()
	return
}

// Class retrieves the class of the object.
func (o *object) Class() (class string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	class, err = o.iface.Class()
	return
}

// GUID retrieves the globally unique identifier of the object.
func (o *object) GUID() (guid uuid.UUID, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		err = ErrClosed
		return
	}

	var sguid string
	sguid, err = o.iface.GUID() // may return binary octet string in hexadecimal form
	if err != nil {
		return
	}

	switch len(sguid) {
	case 38:
		// 38 character representations include dashes and curly braces
		return uuid.Parse(sguid[1:37]) // Omit the braces
	case 32:
		// 32 character representations lack curly braces and dashes. When the
		// LDAP provider returns a GUID as a string in this form, it is the result
		// of taking the binary octet string of the GUID and converting it to
		// hexadecimal.
		_, err = hex.Decode(guid[:], []byte(sguid))
		if err != nil {
			return
		}

		// Assuming the original binary octet string was in the endianness
		// of the originating system, and that the originating system was
		// little-endian, we need to swap the bytes of the GUID we just parsed
		// to put them back in big-endian order.
		guid[0], guid[1], guid[2], guid[3] = guid[3], guid[2], guid[1], guid[0]
		guid[4], guid[5] = guid[5], guid[4]
		guid[6], guid[7] = guid[7], guid[6]
		return
	default:
		return uuid.Parse(sguid)
	}
}

// Path retrieves the fully qualified path of the object.
func (o *object) Path() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	path, err = o.iface.AdsPath()
	return
}

// Parent retrieves the fully qualified path of the object's parent.
func (o *object) Parent() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	path, err = o.iface.Parent()
	return
}

// Schema retrieves the fully qualified path of the object's schema class
// object.
func (o *object) Schema() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	path, err = o.iface.Schema()
	return
}

// Attr attempts to retrieve the attribute with the given name and
// return its values as a slice of interfaces. Each value is an interface{}
// that holds a Go native type that is the best match for the underlying
// variant.
func (o *object) Attr(name string) (values []interface{}, err error) {
	variant, err := o.iface.GetEx(name)
	if err != nil {
		return
	}
	defer variant.Clear()

	array := variant.ToArray()
	if array == nil {
		return nil, ErrNonArrayAttribute
	}

	values, err = comutil.SafeArrayToVariantSlice(array)
	if err != nil {
		err = fmt.Errorf("unable to read \"%s\" attribute: %v", name, err)
	}
	return
}

// AttrStringSlice attempts to retrieve the attribute with the given name and
// return its values as a slice of strings.
//
// Any non-string values contained in the attribute will be ommitted.
func (o *object) AttrStringSlice(name string) (values []string, err error) {
	elements, err := o.Attr(name)
	if err != nil {
		return
	}
	for _, element := range elements {
		if s, ok := element.(string); ok {
			values = append(values, s)
		} else {
			// TODO: Consider returning error
		}
	}
	return
}

// AttrString attempts to retrieve the attribute with the given name and
// return its value as a string. If the attribute holds more than one value,
// only the first value is returned.
//
// Any non-string values contained in the attribute will be ignored.
func (o *object) AttrString(name string) (attr string, err error) {
	array, err := o.AttrStringSlice(name)
	if err != nil {
		return
	}
	if len(array) > 0 {
		return array[0], nil
	}
	return "", nil
}

// AttrBoolSlice attempts to retrieve the attribute with the given name and
// return its values as a slice of bools.
//
// Any non-bool values contained in the attribute will be ommitted.
func (o *object) AttrBoolSlice(name string) (values []bool, err error) {
	elements, err := o.Attr(name)
	if err != nil {
		return
	}
	for _, element := range elements {
		if b, ok := element.(bool); ok {
			values = append(values, b)
		} else {
			// TODO: Consider returning error
		}
	}
	return
}

// AttrBool attempts to retrieve the attribute with the given name and
// return its value as a bool. If the attribute holds more than one value,
// only the first value is returned.
//
// Any non-bool values contained in the attribute will be ignored.
func (o *object) AttrBool(name string) (attr bool, err error) {
	array, err := o.AttrBoolSlice(name)
	if err != nil {
		return
	}
	if len(array) > 0 {
		return array[0], nil
	}
	return false, nil
}

// ToContainer attempts to acquire a container interface for the object.
func (o *object) ToContainer() (c *Container, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	idispatch, err := o.iface.QueryInterface(comutil.GUID(comiid.IADsContainer))
	if err != nil {
		return
	}
	iface := (*api.IADsContainer)(unsafe.Pointer(idispatch))
	c = NewContainer(iface)
	return
}

// ToComputer attempts to acquire a computer interface for the object.
func (o *object) ToComputer() (c *Computer, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	idispatch, err := o.iface.QueryInterface(comutil.GUID(comiid.IADsComputer))
	if err != nil {
		return
	}
	iface := (*api.IADsComputer)(unsafe.Pointer(idispatch))
	c = NewComputer(iface)
	return
}

// ToGroup attempts to acquire a group interface for the object.
func (o *object) ToGroup() (g *Group, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	idispatch, err := o.iface.QueryInterface(comutil.GUID(comiid.IADsGroup))
	if err != nil {
		return
	}
	iface := (*api.IADsGroup)(unsafe.Pointer(idispatch))
	g = NewGroup(iface)
	return
}
<commit_msg>Added support for []byte and uuid/guid attribute parsing<commit_after>package adsi

import (
	"encoding/hex"
	"fmt"
	"sync"
	"unsafe"

	"github.com/google/uuid"
	"github.com/scjalliance/comshim"
	"github.com/scjalliance/comutil"
	"gopkg.in/adsi.v0/api"
	"gopkg.in/adsi.v0/comiid"
)

// ADSI Objects of LDAP:  https://msdn.microsoft.com/library/aa772208
// ADSI Objects of WinNT: https://msdn.microsoft.com/library/aa772211

// Object provides access to Active Directory objects.
type Object struct {
	object
}

// NewObject returns an object that manages the given COM interface.
func NewObject(iface *api.IADs) *Object {
	comshim.Add(1)
	return &Object{object{iface: iface}}
}

type object struct {
	m     sync.RWMutex
	iface *api.IADs
}

func (o *object) closed() bool {
	return (o.iface == nil)
}

// Close will release resources consumed by the object. It should be
// called when the object is no longer needed.
func (o *object) Close() {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return
	}
	defer comshim.Done()
	o.iface.Release() // FIXME: What happens if release returns an error?
	o.iface = nil
}

// Name retrieves the name of the object.
func (o *object) Name() (name string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	name, err = o.iface.Name()
	return
}

// Class retrieves the class of the object.
func (o *object) Class() (class string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	class, err = o.iface.Class()
	return
}

// GUID retrieves the globally unique identifier of the object.
func (o *object) GUID() (guid uuid.UUID, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		err = ErrClosed
		return
	}

	var sguid string
	sguid, err = o.iface.GUID() // may return binary octet string in hexadecimal form
	if err != nil {
		return
	}

	switch len(sguid) {
	case 38:
		// 38 character representations include dashes and curly braces
		return uuid.Parse(sguid[1:37]) // Omit the braces
	case 32:
		// 32 character representations lack curly braces and dashes. When the
		// LDAP provider returns a GUID as a string in this form, it is the result
		// of taking the binary octet string of the GUID and converting it to
		// hexadecimal.
		_, err = hex.Decode(guid[:], []byte(sguid))
		if err != nil {
			return
		}

		// Assuming the original binary octet string was in the endianness
		// of the originating system, and that the originating system was
		// little-endian, we need to swap the bytes of the GUID we just parsed
		// to put them back in big-endian order.
		guid[0], guid[1], guid[2], guid[3] = guid[3], guid[2], guid[1], guid[0]
		guid[4], guid[5] = guid[5], guid[4]
		guid[6], guid[7] = guid[7], guid[6]
		return
	default:
		return uuid.Parse(sguid)
	}
}

// Path retrieves the fully qualified path of the object.
func (o *object) Path() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	path, err = o.iface.AdsPath()
	return
}

// Parent retrieves the fully qualified path of the object's parent.
func (o *object) Parent() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	path, err = o.iface.Parent()
	return
}

// Schema retrieves the fully qualified path of the object's schema class
// object.
func (o *object) Schema() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	path, err = o.iface.Schema()
	return
}

// Attr attempts to retrieve the attribute with the given name and
// return its values as a slice of interfaces. Each value is an interface{}
// that holds a Go native type that is the best match for the underlying
// variant.
func (o *object) Attr(name string) (values []interface{}, err error) {
	variant, err := o.iface.GetEx(name)
	if err != nil {
		return
	}
	defer variant.Clear()

	array := variant.ToArray()
	if array == nil {
		return nil, ErrNonArrayAttribute
	}

	values, err = comutil.SafeArrayToVariantSlice(array)
	if err != nil {
		err = fmt.Errorf("unable to read \"%s\" attribute: %v", name, err)
	}
	return
}

// AttrStringSlice attempts to retrieve the attribute with the given name and
// return its values as a slice of strings.
//
// Any non-string values contained in the attribute will be ommitted.
func (o *object) AttrStringSlice(name string) (values []string, err error) {
	elements, err := o.Attr(name)
	if err != nil {
		return
	}
	for _, element := range elements {
		if s, ok := element.(string); ok {
			values = append(values, s)
		} else {
			// TODO: Consider returning error
		}
	}
	return
}

// AttrString attempts to retrieve the attribute with the given name and
// return its value as a string. If the attribute holds more than one value,
// only the first value is returned.
//
// Any non-string values contained in the attribute will be ignored.
func (o *object) AttrString(name string) (attr string, err error) {
	array, err := o.AttrStringSlice(name)
	if err != nil {
		return
	}
	if len(array) > 0 {
		return array[0], nil
	}
	return "", nil
}

// AttrBytesSlice attempts to retrieve the attribute with the given name and
// return its values as a slice of strings.
//
// Any non-string values contained in the attribute will be ommitted.
func (o *object) AttrBytesSlice(name string) (values [][]byte, err error) {
	elements, err := o.Attr(name)
	if err != nil {
		return
	}
	for _, element := range elements {
		if data, ok := element.([]byte); ok {
			values = append(values, data)
		} else {
			// TODO: Consider returning error
		}
	}
	return
}

// AttrBytes attempts to retrieve the attribute with the given name and
// return its value as a string. If the attribute holds more than one value,
// only the first value is returned.
//
// Any non-string values contained in the attribute will be ignored.
func (o *object) AttrBytes(name string) (attr []byte, err error) {
	array, err := o.AttrBytesSlice(name)
	if err != nil {
		return
	}
	if len(array) > 0 {
		return array[0], nil
	}
	return nil, nil
}

// AttrBoolSlice attempts to retrieve the attribute with the given name and
// return its values as a slice of bools.
//
// Any non-bool values contained in the attribute will be ommitted.
func (o *object) AttrBoolSlice(name string) (values []bool, err error) {
	elements, err := o.Attr(name)
	if err != nil {
		return
	}
	for _, element := range elements {
		if b, ok := element.(bool); ok {
			values = append(values, b)
		} else {
			// TODO: Consider returning error
		}
	}
	return
}

// AttrBool attempts to retrieve the attribute with the given name and
// return its value as a bool. If the attribute holds more than one value,
// only the first value is returned.
//
// Any non-bool values contained in the attribute will be ignored.
func (o *object) AttrBool(name string) (attr bool, err error) {
	array, err := o.AttrBoolSlice(name)
	if err != nil {
		return
	}
	if len(array) > 0 {
		return array[0], nil
	}
	return false, nil
}

// AttrGUIDSlice attempts to retrieve the attribute with the given name and
// return its values as a slice of GUIDs.
//
// Any non-GUID values contained in the attribute will be ommitted.
//
// Values are returned as-is, without any byte ordering adjustment.
func (o *object) AttrGUIDSlice(name string) (values []uuid.UUID, err error) {
	elements, err := o.Attr(name)
	if err != nil {
		return
	}
	for _, element := range elements {
		switch e := element.(type) {
		case string:
			value, parseErr := uuid.Parse(e)
			if parseErr == nil {
				values = append(values, value)
			} else {
				// TODO: Consider returning error
			}
		case []byte:
			if len(e) == 16 {
				value, parseErr := uuid.FromBytes(e)
				if parseErr == nil {
					values = append(values, value)
				} else {
					// TODO: Consider returning error
				}
			} else {
				// TODO: Consider returning error
			}
		default:
			// TODO: Consider returning error
		}
	}
	return
}

// AttrGUID attempts to retrieve the attribute with the given name and
// return its value as a GUID in string format. If the attribute holds more
// than one value, only the first value is returned.
//
// Any non-string values contained in the attribute will be ignored.
func (o *object) AttrGUID(name string) (attr uuid.UUID, err error) {
	array, err := o.AttrGUIDSlice(name)
	if err != nil {
		return
	}
	if len(array) > 0 {
		attr = array[0]
	}
	return
}

// ToContainer attempts to acquire a container interface for the object.
func (o *object) ToContainer() (c *Container, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	idispatch, err := o.iface.QueryInterface(comutil.GUID(comiid.IADsContainer))
	if err != nil {
		return
	}
	iface := (*api.IADsContainer)(unsafe.Pointer(idispatch))
	c = NewContainer(iface)
	return
}

// ToComputer attempts to acquire a computer interface for the object.
func (o *object) ToComputer() (c *Computer, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	idispatch, err := o.iface.QueryInterface(comutil.GUID(comiid.IADsComputer))
	if err != nil {
		return
	}
	iface := (*api.IADsComputer)(unsafe.Pointer(idispatch))
	c = NewComputer(iface)
	return
}

// ToGroup attempts to acquire a group interface for the object.
func (o *object) ToGroup() (g *Group, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	idispatch, err := o.iface.QueryInterface(comutil.GUID(comiid.IADsGroup))
	if err != nil {
		return
	}
	iface := (*api.IADsGroup)(unsafe.Pointer(idispatch))
	g = NewGroup(iface)
	return
}
<|endoftext|>


<commit_before>package indicators

import (
	"errors"
	"github.com/thetruetrade/gotrade"
)

// An Average Directional Index (Adx), no storage, for use in other indicators
type AdxWithoutStorage struct {
	*baseIndicator
	*baseFloatBounds

	// private variables
	valueAvailableAction ValueAvailableActionFloat
	periodCounter        int
	dx                   *DXWithoutStorage
	currentDX            float64
	sumDX                float64
	previousAdx          float64
	timePeriod           int
}

// NewAdxWithoutStorage creates an Average Directional Index (Adx) without storage
func NewAdxWithoutStorage(timePeriod int, valueAvailableAction ValueAvailableActionFloat) (indicator *AdxWithoutStorage, err error) {

	// an indicator without storage MUST have a value available action
	if valueAvailableAction == nil {
		return nil, ErrValueAvailableActionIsNil
	}

	// the minimum timeperiod for an Adx indicator is 2
	if timePeriod < 2 {
		return nil, errors.New("timePeriod is less than the minimum (2)")
	}

	// check the maximum timeperiod
	if timePeriod > MaximumLookbackPeriod {
		return nil, errors.New("timePeriod is greater than the maximum (100000)")
	}

	lookback := (2 * timePeriod) - 1
	ind := AdxWithoutStorage{
		baseIndicator:        newBaseIndicator(lookback),
		baseFloatBounds:      newBaseFloatBounds(),
		timePeriod:           timePeriod,
		periodCounter:        timePeriod * -1,
		currentDX:            0.0,
		sumDX:                0.0,
		previousAdx:          0.0,
		valueAvailableAction: valueAvailableAction,
	}

	ind.dx, err = NewDXWithoutStorage(timePeriod, func(dataItem float64, streamBarIndex int) {

		ind.currentDX = dataItem

		ind.periodCounter += 1
		if ind.periodCounter < 0 {
			ind.sumDX += ind.currentDX
		} else if ind.periodCounter == 0 {
			ind.dataLength += 1

			if ind.validFromBar == -1 {
				// set the streamBarIndex from which this indicator returns valid results
				ind.validFromBar = streamBarIndex
			}

			ind.sumDX += ind.currentDX
			result := ind.sumDX / float64(ind.GetTimePeriod())

			// update the maximum result value
			if result > ind.maxValue {
				ind.maxValue = result
			}

			// update the minimum result value
			if result < ind.minValue {
				ind.minValue = result
			}
			ind.valueAvailableAction(result, streamBarIndex)
			ind.previousAdx = result

		} else {

			ind.dataLength += 1

			result := (ind.previousAdx*float64(ind.GetTimePeriod()-1) + ind.currentDX) / float64(ind.GetTimePeriod())

			// update the maximum result value
			if result > ind.maxValue {
				ind.maxValue = result
			}

			// update the minimum result value
			if result < ind.minValue {
				ind.minValue = result
			}
			// notify of a new result value though the value available action
			ind.valueAvailableAction(result, streamBarIndex)

			ind.previousAdx = result
		}

	})

	return &ind, err
}

// A Directional Movement Indicator (Adx)
type Adx struct {
	*AdxWithoutStorage

	// public variables
	Data []float64
}

// NewAdx creates an Average Directional Index (Adx) for online usage
func NewAdx(timePeriod int) (indicator *Adx, err error) {

	ind := Adx{}
	ind.AdxWithoutStorage, err = NewAdxWithoutStorage(timePeriod,
		func(dataItem float64, streamBarIndex int) {
			ind.Data = append(ind.Data, dataItem)
		})

	return &ind, err
}

// NewDefaultAdx creates an Average Directional Index (Adx) for online usage with default parameters
//	- timePeriod: 14
func NewDefaultAdx() (indicator *Adx, err error) {
	timePeriod := 14
	return NewAdx(timePeriod)
}

// NewAdxWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage
func NewAdxWithKnownSourceLength(sourceLength int, timePeriod int) (indicator *Adx, err error) {
	ind, err := NewAdx(timePeriod)
	ind.Data = make([]float64, 0, sourceLength)

	return ind, err
}

// NewDefaultAdxWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage with default parameters
func NewDefaultAdxWithKnownSourceLength(sourceLength int) (indicator *Adx, err error) {

	ind, err := NewDefaultAdx()
	ind.Data = make([]float64, 0, sourceLength)
	return ind, err
}

// NewAdxForStream creates an Average Directional Index (Adx) for online usage with a source data stream
func NewAdxForStream(priceStream *gotrade.DOHLCVStream, timePeriod int) (indicator *Adx, err error) {
	ind, err := NewAdx(timePeriod)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewADefaultDXForStream creates an Average Directional Index (Adx) for online usage with a source data stream
func NewDefaultAdxForStream(priceStream *gotrade.DOHLCVStream) (indicator *Adx, err error) {
	ind, err := NewDefaultAdx()
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewAdxForStreamWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage with a source data stream
func NewAdxForStreamWithKnownSourceLength(sourceLength int, priceStream *gotrade.DOHLCVStream, timePeriod int) (indicator *Adx, err error) {
	ind, err := NewAdxWithKnownSourceLength(sourceLength, timePeriod)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDefaultAdxForStreamWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage with a source data stream
func NewDefaultAdxForStreamWithKnownSourceLength(sourceLength int, priceStream *gotrade.DOHLCVStream) (indicator *Adx, err error) {
	ind, err := NewDefaultAdxWithKnownSourceLength(sourceLength)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// GetTimePeriod returns the configured Adx timePeriod
func (ind *AdxWithoutStorage) GetTimePeriod() int {
	return ind.timePeriod
}

// ReceiveDOHLCVTick consumes a source data DOHLCV price tick
func (ind *AdxWithoutStorage) ReceiveDOHLCVTick(tickData gotrade.DOHLCV, streamBarIndex int) {
	ind.dx.ReceiveDOHLCVTick(tickData, streamBarIndex)
}
<commit_msg>fixed typo in comment<commit_after>package indicators

import (
	"errors"
	"github.com/thetruetrade/gotrade"
)

// An Average Directional Index (Adx), no storage, for use in other indicators
type AdxWithoutStorage struct {
	*baseIndicator
	*baseFloatBounds

	// private variables
	valueAvailableAction ValueAvailableActionFloat
	periodCounter        int
	dx                   *DXWithoutStorage
	currentDX            float64
	sumDX                float64
	previousAdx          float64
	timePeriod           int
}

// NewAdxWithoutStorage creates an Average Directional Index (Adx) without storage
func NewAdxWithoutStorage(timePeriod int, valueAvailableAction ValueAvailableActionFloat) (indicator *AdxWithoutStorage, err error) {

	// an indicator without storage MUST have a value available action
	if valueAvailableAction == nil {
		return nil, ErrValueAvailableActionIsNil
	}

	// the minimum timeperiod for an Adx indicator is 2
	if timePeriod < 2 {
		return nil, errors.New("timePeriod is less than the minimum (2)")
	}

	// check the maximum timeperiod
	if timePeriod > MaximumLookbackPeriod {
		return nil, errors.New("timePeriod is greater than the maximum (100000)")
	}

	lookback := (2 * timePeriod) - 1
	ind := AdxWithoutStorage{
		baseIndicator:        newBaseIndicator(lookback),
		baseFloatBounds:      newBaseFloatBounds(),
		timePeriod:           timePeriod,
		periodCounter:        timePeriod * -1,
		currentDX:            0.0,
		sumDX:                0.0,
		previousAdx:          0.0,
		valueAvailableAction: valueAvailableAction,
	}

	ind.dx, err = NewDXWithoutStorage(timePeriod, func(dataItem float64, streamBarIndex int) {

		ind.currentDX = dataItem

		ind.periodCounter += 1
		if ind.periodCounter < 0 {
			ind.sumDX += ind.currentDX
		} else if ind.periodCounter == 0 {
			ind.dataLength += 1

			if ind.validFromBar == -1 {
				// set the streamBarIndex from which this indicator returns valid results
				ind.validFromBar = streamBarIndex
			}

			ind.sumDX += ind.currentDX
			result := ind.sumDX / float64(ind.GetTimePeriod())

			// update the maximum result value
			if result > ind.maxValue {
				ind.maxValue = result
			}

			// update the minimum result value
			if result < ind.minValue {
				ind.minValue = result
			}
			ind.valueAvailableAction(result, streamBarIndex)
			ind.previousAdx = result

		} else {

			ind.dataLength += 1

			result := (ind.previousAdx*float64(ind.GetTimePeriod()-1) + ind.currentDX) / float64(ind.GetTimePeriod())

			// update the maximum result value
			if result > ind.maxValue {
				ind.maxValue = result
			}

			// update the minimum result value
			if result < ind.minValue {
				ind.minValue = result
			}
			// notify of a new result value though the value available action
			ind.valueAvailableAction(result, streamBarIndex)

			ind.previousAdx = result
		}

	})

	return &ind, err
}

// A Directional Movement Indicator (Adx)
type Adx struct {
	*AdxWithoutStorage

	// public variables
	Data []float64
}

// NewAdx creates an Average Directional Index (Adx) for online usage
func NewAdx(timePeriod int) (indicator *Adx, err error) {

	ind := Adx{}
	ind.AdxWithoutStorage, err = NewAdxWithoutStorage(timePeriod,
		func(dataItem float64, streamBarIndex int) {
			ind.Data = append(ind.Data, dataItem)
		})

	return &ind, err
}

// NewDefaultAdx creates an Average Directional Index (Adx) for online usage with default parameters
//	- timePeriod: 14
func NewDefaultAdx() (indicator *Adx, err error) {
	timePeriod := 14
	return NewAdx(timePeriod)
}

// NewAdxWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage
func NewAdxWithKnownSourceLength(sourceLength int, timePeriod int) (indicator *Adx, err error) {
	ind, err := NewAdx(timePeriod)
	ind.Data = make([]float64, 0, sourceLength)

	return ind, err
}

// NewDefaultAdxWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage with default parameters
func NewDefaultAdxWithKnownSourceLength(sourceLength int) (indicator *Adx, err error) {

	ind, err := NewDefaultAdx()
	ind.Data = make([]float64, 0, sourceLength)
	return ind, err
}

// NewAdxForStream creates an Average Directional Index (Adx) for online usage with a source data stream
func NewAdxForStream(priceStream *gotrade.DOHLCVStream, timePeriod int) (indicator *Adx, err error) {
	ind, err := NewAdx(timePeriod)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDefaultAdxForStream creates an Average Directional Index (Adx) for online usage with a source data stream
func NewDefaultAdxForStream(priceStream *gotrade.DOHLCVStream) (indicator *Adx, err error) {
	ind, err := NewDefaultAdx()
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewAdxForStreamWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage with a source data stream
func NewAdxForStreamWithKnownSourceLength(sourceLength int, priceStream *gotrade.DOHLCVStream, timePeriod int) (indicator *Adx, err error) {
	ind, err := NewAdxWithKnownSourceLength(sourceLength, timePeriod)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDefaultAdxForStreamWithKnownSourceLength creates an Average Directional Index (Adx) for offline usage with a source data stream
func NewDefaultAdxForStreamWithKnownSourceLength(sourceLength int, priceStream *gotrade.DOHLCVStream) (indicator *Adx, err error) {
	ind, err := NewDefaultAdxWithKnownSourceLength(sourceLength)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// GetTimePeriod returns the configured Adx timePeriod
func (ind *AdxWithoutStorage) GetTimePeriod() int {
	return ind.timePeriod
}

// ReceiveDOHLCVTick consumes a source data DOHLCV price tick
func (ind *AdxWithoutStorage) ReceiveDOHLCVTick(tickData gotrade.DOHLCV, streamBarIndex int) {
	ind.dx.ReceiveDOHLCVTick(tickData, streamBarIndex)
}
<|endoftext|>


<commit_before>// +build ignore

package linode

import (
	"context"
	"fmt"
	"strconv"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/linode/linodego"
)

func TestAccLinodeTemplate_basic(t *testing.T) {
	t.Parallel()

	resName := "linode_template.foobar"
	var templateName = acctest.RandomWithPrefix("tf_test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLinodeTemplateDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLinodeTemplateConfigBasic(templateName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLinodeTemplateExists,
					resource.TestCheckResourceAttr(resName, "label", templateName),
				),
			},
			{
				ResourceName:      resName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccLinodeTemplate_update(t *testing.T) {
	t.Parallel()

	var templateName = acctest.RandomWithPrefix("tf_test")
	resName := "linode_template.foobar"

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLinodeTemplateDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLinodeTemplateConfigBasic(templateName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLinodeTemplateExists,
					resource.TestCheckResourceAttr(resName, "label", templateName),
				),
			},
			{
				Config: testAccCheckLinodeTemplateConfigUpdates(templateName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLinodeTemplateExists,
					resource.TestCheckResourceAttr(resName, "label", fmt.Sprintf("%s_renamed", templateName)),
				),
			},
		},
	})
}

func testAccCheckLinodeTemplateExists(s *terraform.State) error {
	client := testAccProvider.Meta().(linodego.Client)

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "linode_template" {
			continue
		}

		id, err := strconv.Atoi(rs.Primary.ID)
		if err != nil {
			return fmt.Errorf("Error parsing %v to int", rs.Primary.ID)
		}

		_, err = client.GetTemplate(context.Background(), id)
		if err != nil {
			return fmt.Errorf("Error retrieving state of Template %s: %s", rs.Primary.Attributes["label"], err)
		}
	}

	return nil
}

func testAccCheckLinodeTemplateDestroy(s *terraform.State) error {
	client, ok := testAccProvider.Meta().(linodego.Client)
	if !ok {
		return fmt.Errorf("Error getting Linode client")
	}
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "linode_template" {
			continue
		}

		id, err := strconv.Atoi(rs.Primary.ID)
		if err != nil {
			return fmt.Errorf("Error parsing %v to int", rs.Primary.ID)
		}
		if id == 0 {
			return fmt.Errorf("Would have considered %v as %d", rs.Primary.ID, id)

		}

		_, err = client.GetTemplate(id)

		if err == nil {
			return fmt.Errorf("Linode Template with id %d still exists", id)
		}

		if apiErr, ok := err.(*linodego.Error); ok && apiErr.Code != 404 {
			return fmt.Errorf("Error requesting Linode Template with id %d", id)
		}
	}

	return nil
}

func testAccCheckLinodeTemplateConfigBasic(template string) string {
	return fmt.Sprintf(`
resource "linode_template" "foobar" {
	label = "%s"
}`, template)
}

func testAccCheckLinodeTemplateConfigUpdates(template string) string {
	return fmt.Sprintf(`
resource "linode_template" "foobar" {
	label = "%s_renamed"
}`, template)
}
<commit_msg>fix the arguments to Get in the template<commit_after>// +build ignore

package linode

import (
	"context"
	"fmt"
	"strconv"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/linode/linodego"
)

func TestAccLinodeTemplate_basic(t *testing.T) {
	t.Parallel()

	resName := "linode_template.foobar"
	var templateName = acctest.RandomWithPrefix("tf_test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLinodeTemplateDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLinodeTemplateConfigBasic(templateName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLinodeTemplateExists,
					resource.TestCheckResourceAttr(resName, "label", templateName),
				),
			},
			{
				ResourceName:      resName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccLinodeTemplate_update(t *testing.T) {
	t.Parallel()

	var templateName = acctest.RandomWithPrefix("tf_test")
	resName := "linode_template.foobar"

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLinodeTemplateDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLinodeTemplateConfigBasic(templateName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLinodeTemplateExists,
					resource.TestCheckResourceAttr(resName, "label", templateName),
				),
			},
			{
				Config: testAccCheckLinodeTemplateConfigUpdates(templateName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLinodeTemplateExists,
					resource.TestCheckResourceAttr(resName, "label", fmt.Sprintf("%s_renamed", templateName)),
				),
			},
		},
	})
}

func testAccCheckLinodeTemplateExists(s *terraform.State) error {
	client := testAccProvider.Meta().(linodego.Client)

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "linode_template" {
			continue
		}

		id, err := strconv.Atoi(rs.Primary.ID)
		if err != nil {
			return fmt.Errorf("Error parsing %v to int", rs.Primary.ID)
		}

		_, err = client.GetTemplate(context.Background(), id)
		if err != nil {
			return fmt.Errorf("Error retrieving state of Template %s: %s", rs.Primary.Attributes["label"], err)
		}
	}

	return nil
}

func testAccCheckLinodeTemplateDestroy(s *terraform.State) error {
	client, ok := testAccProvider.Meta().(linodego.Client)
	if !ok {
		return fmt.Errorf("Error getting Linode client")
	}
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "linode_template" {
			continue
		}

		id, err := strconv.Atoi(rs.Primary.ID)
		if err != nil {
			return fmt.Errorf("Error parsing %v to int", rs.Primary.ID)
		}
		if id == 0 {
			return fmt.Errorf("Would have considered %v as %d", rs.Primary.ID, id)

		}

		_, err = client.GetTemplate(context.Background(), id)

		if err == nil {
			return fmt.Errorf("Linode Template with id %d still exists", id)
		}

		if apiErr, ok := err.(*linodego.Error); ok && apiErr.Code != 404 {
			return fmt.Errorf("Error requesting Linode Template with id %d", id)
		}
	}

	return nil
}

func testAccCheckLinodeTemplateConfigBasic(template string) string {
	return fmt.Sprintf(`
resource "linode_template" "foobar" {
	label = "%s"
}`, template)
}

func testAccCheckLinodeTemplateConfigUpdates(template string) string {
	return fmt.Sprintf(`
resource "linode_template" "foobar" {
	label = "%s_renamed"
}`, template)
}
<|endoftext|>


<commit_before>package arel

type InsertManager struct {
	AstNode
}
<commit_msg>InsertManager should use BaseNode<commit_after>package arel

type InsertManager struct {
	BaseNode
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Package dl implements a simple downloads frontend server.
//
// It accepts HTTP POST requests to create a new download metadata entity, and
// lists entities with sorting and filtering.
// It is designed to run only on the instance of godoc that serves golang.org.
//
// The package also serves the list of downloads at:
//     https://golang.org/dl/
//
// An optional query param, mode=json, serves the list of stable release
// downloads in JSON format:
//     https://golang.org/dl/?mode=json
//
// An additional query param, include=all, when used with the mode=json
// query param, will serve a full list of available downloads, including
// stable, unstable, and archived releases in JSON format:
//     https://golang.org/dl/?mode=json&include=all
package dl

import (
	"fmt"
	"html/template"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	downloadBaseURL = "https://dl.google.com/go/"
	cacheKey        = "download_list_4" // increment if listTemplateData changes
	cacheDuration   = time.Hour
)

// File represents a file on the golang.org downloads page.
// It should be kept in sync with the upload code in x/build/cmd/release.
type File struct {
	Filename       string    `json:"filename"`
	OS             string    `json:"os"`
	Arch           string    `json:"arch"`
	Version        string    `json:"version"`
	Checksum       string    `json:"-" datastore:",noindex"` // SHA1; deprecated
	ChecksumSHA256 string    `json:"sha256" datastore:",noindex"`
	Size           int64     `json:"size" datastore:",noindex"`
	Kind           string    `json:"kind"` // "archive", "installer", "source"
	Uploaded       time.Time `json:"-"`
}

func (f File) ChecksumType() string {
	if f.ChecksumSHA256 != "" {
		return "SHA256"
	}
	return "SHA1"
}

func (f File) PrettyChecksum() string {
	if f.ChecksumSHA256 != "" {
		return f.ChecksumSHA256
	}
	return f.Checksum
}

func (f File) PrettyOS() string {
	if f.OS == "darwin" {
		switch {
		case strings.Contains(f.Filename, "osx10.8"):
			return "OS X 10.8+"
		case strings.Contains(f.Filename, "osx10.6"):
			return "OS X 10.6+"
		}
	}
	return pretty(f.OS)
}

func (f File) PrettySize() string {
	const mb = 1 << 20
	if f.Size == 0 {
		return ""
	}
	if f.Size < mb {
		// All Go releases are >1mb, but handle this case anyway.
		return fmt.Sprintf("%v bytes", f.Size)
	}
	return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
}

var primaryPorts = map[string]bool{
	"darwin/amd64":  true,
	"linux/386":     true,
	"linux/amd64":   true,
	"linux/armv6l":  true,
	"windows/386":   true,
	"windows/amd64": true,
}

func (f File) PrimaryPort() bool {
	if f.Kind == "source" {
		return true
	}
	return primaryPorts[f.OS+"/"+f.Arch]
}

func (f File) Highlight() bool {
	switch {
	case f.Kind == "source":
		return true
	case f.Arch == "amd64" && f.OS == "linux":
		return true
	case f.Arch == "amd64" && f.Kind == "installer":
		switch f.OS {
		case "windows":
			return true
		case "darwin":
			if !strings.Contains(f.Filename, "osx10.6") {
				return true
			}
		}
	}
	return false
}

func (f File) URL() string {
	return downloadBaseURL + f.Filename
}

type Release struct {
	Version        string `json:"version"`
	Stable         bool   `json:"stable"`
	Files          []File `json:"files"`
	Visible        bool   `json:"-"` // show files on page load
	SplitPortTable bool   `json:"-"` // whether files should be split by primary/other ports.
}

type Feature struct {
	// The File field will be filled in by the first stable File
	// whose name matches the given fileRE.
	File
	fileRE *regexp.Regexp

	Platform     string // "Microsoft Windows", "Apple macOS", "Linux"
	Requirements string // "Windows XP and above, 64-bit Intel Processor"
}

// featuredFiles lists the platforms and files to be featured
// at the top of the downloads page.
var featuredFiles = []Feature{
	{
		Platform:     "Microsoft Windows",
		Requirements: "Windows 7 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.windows-amd64\.msi$`),
	},
	{
		Platform:     "Apple macOS",
		Requirements: "macOS 10.10 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
	},
	{
		Platform:     "Linux",
		Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
	},
	{
		Platform: "Source",
		fileRE:   regexp.MustCompile(`\.src\.tar\.gz$`),
	},
}

// data to send to the template; increment cacheKey if you change this.
type listTemplateData struct {
	Featured                  []Feature
	Stable, Unstable, Archive []Release
	GoogleCN                  bool
}

var (
	listTemplate  = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
	templateFuncs = template.FuncMap{"pretty": pretty}
)

func filesToFeatured(fs []File) (featured []Feature) {
	for _, feature := range featuredFiles {
		for _, file := range fs {
			if feature.fileRE.MatchString(file.Filename) {
				feature.File = file
				featured = append(featured, feature)
				break
			}
		}
	}
	return
}

func filesToReleases(fs []File) (stable, unstable, archive []Release) {
	sort.Sort(fileOrder(fs))

	var r *Release
	var stableMaj, stableMin int
	add := func() {
		if r == nil {
			return
		}
		if !r.Stable {
			if len(unstable) != 0 {
				// Only show one (latest) unstable version.
				return
			}
			maj, min, _ := parseVersion(r.Version)
			if maj < stableMaj || maj == stableMaj && min <= stableMin {
				// Display unstable version only if newer than the
				// latest stable release.
				return
			}
			unstable = append(unstable, *r)
			return
		}

		// Reports whether the release is the most recent minor version of the
		// two most recent major versions.
		shouldAddStable := func() bool {
			if len(stable) >= 2 {
				// Show up to two stable versions.
				return false
			}
			if len(stable) == 0 {
				// Most recent stable version.
				stableMaj, stableMin, _ = parseVersion(r.Version)
				return true
			}
			if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
				// Older minor version of most recent major version.
				return false
			}
			// Second most recent stable version.
			return true
		}
		if !shouldAddStable() {
			archive = append(archive, *r)
			return
		}

		// Split the file list into primary/other ports for the stable releases.
		// NOTE(cbro): This is only done for stable releases because maintaining the historical
		// nature of primary/other ports for older versions is infeasible.
		// If freebsd is considered primary some time in the future, we'd not want to
		// mark all of the older freebsd binaries as "primary".
		// It might be better if we set that as a flag when uploading.
		r.SplitPortTable = true
		r.Visible = true // Toggle open all stable releases.
		stable = append(stable, *r)
	}
	for _, f := range fs {
		if r == nil || f.Version != r.Version {
			add()
			r = &Release{
				Version: f.Version,
				Stable:  isStable(f.Version),
			}
		}
		r.Files = append(r.Files, f)
	}
	add()
	return
}

// isStable reports whether the version string v is a stable version.
func isStable(v string) bool {
	return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
}

type fileOrder []File

func (s fileOrder) Len() int      { return len(s) }
func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s fileOrder) Less(i, j int) bool {
	a, b := s[i], s[j]
	if av, bv := a.Version, b.Version; av != bv {
		return versionLess(av, bv)
	}
	if a.OS != b.OS {
		return a.OS < b.OS
	}
	if a.Arch != b.Arch {
		return a.Arch < b.Arch
	}
	if a.Kind != b.Kind {
		return a.Kind < b.Kind
	}
	return a.Filename < b.Filename
}

func versionLess(a, b string) bool {
	// Put stable releases first.
	if isStable(a) != isStable(b) {
		return isStable(a)
	}
	maja, mina, ta := parseVersion(a)
	majb, minb, tb := parseVersion(b)
	if maja == majb {
		if mina == minb {
			return ta >= tb
		}
		return mina >= minb
	}
	return maja >= majb
}

func parseVersion(v string) (maj, min int, tail string) {
	if i := strings.Index(v, "beta"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	if i := strings.Index(v, "rc"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
	maj, _ = strconv.Atoi(p[0])
	if len(p) < 2 {
		return
	}
	min, _ = strconv.Atoi(p[1])
	return
}

func validUser(user string) bool {
	switch user {
	case "adg", "bradfitz", "cbro", "andybons", "valsorda", "dmitshur", "katiehockman", "julieqiu", "rakoczy":
		return true
	}
	return false
}

var (
	fileRe  = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
	goGetRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+$`)
)

// pretty returns a human-readable version of the given OS, Arch, or Kind.
func pretty(s string) string {
	t, ok := prettyStrings[s]
	if !ok {
		return s
	}
	return t
}

var prettyStrings = map[string]string{
	"darwin":  "macOS",
	"freebsd": "FreeBSD",
	"linux":   "Linux",
	"windows": "Windows",

	"386":    "x86",
	"amd64":  "x86-64",
	"armv6l": "ARMv6",
	"arm64":  "ARMv8",

	"archive":   "Archive",
	"installer": "Installer",
	"source":    "Source",
}
<commit_msg>internal/dl: update minimum macOS version to 10.11<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Package dl implements a simple downloads frontend server.
//
// It accepts HTTP POST requests to create a new download metadata entity, and
// lists entities with sorting and filtering.
// It is designed to run only on the instance of godoc that serves golang.org.
//
// The package also serves the list of downloads at:
//     https://golang.org/dl/
//
// An optional query param, mode=json, serves the list of stable release
// downloads in JSON format:
//     https://golang.org/dl/?mode=json
//
// An additional query param, include=all, when used with the mode=json
// query param, will serve a full list of available downloads, including
// stable, unstable, and archived releases in JSON format:
//     https://golang.org/dl/?mode=json&include=all
package dl

import (
	"fmt"
	"html/template"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	downloadBaseURL = "https://dl.google.com/go/"
	cacheKey        = "download_list_4" // increment if listTemplateData changes
	cacheDuration   = time.Hour
)

// File represents a file on the golang.org downloads page.
// It should be kept in sync with the upload code in x/build/cmd/release.
type File struct {
	Filename       string    `json:"filename"`
	OS             string    `json:"os"`
	Arch           string    `json:"arch"`
	Version        string    `json:"version"`
	Checksum       string    `json:"-" datastore:",noindex"` // SHA1; deprecated
	ChecksumSHA256 string    `json:"sha256" datastore:",noindex"`
	Size           int64     `json:"size" datastore:",noindex"`
	Kind           string    `json:"kind"` // "archive", "installer", "source"
	Uploaded       time.Time `json:"-"`
}

func (f File) ChecksumType() string {
	if f.ChecksumSHA256 != "" {
		return "SHA256"
	}
	return "SHA1"
}

func (f File) PrettyChecksum() string {
	if f.ChecksumSHA256 != "" {
		return f.ChecksumSHA256
	}
	return f.Checksum
}

func (f File) PrettyOS() string {
	if f.OS == "darwin" {
		switch {
		case strings.Contains(f.Filename, "osx10.8"):
			return "OS X 10.8+"
		case strings.Contains(f.Filename, "osx10.6"):
			return "OS X 10.6+"
		}
	}
	return pretty(f.OS)
}

func (f File) PrettySize() string {
	const mb = 1 << 20
	if f.Size == 0 {
		return ""
	}
	if f.Size < mb {
		// All Go releases are >1mb, but handle this case anyway.
		return fmt.Sprintf("%v bytes", f.Size)
	}
	return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
}

var primaryPorts = map[string]bool{
	"darwin/amd64":  true,
	"linux/386":     true,
	"linux/amd64":   true,
	"linux/armv6l":  true,
	"windows/386":   true,
	"windows/amd64": true,
}

func (f File) PrimaryPort() bool {
	if f.Kind == "source" {
		return true
	}
	return primaryPorts[f.OS+"/"+f.Arch]
}

func (f File) Highlight() bool {
	switch {
	case f.Kind == "source":
		return true
	case f.Arch == "amd64" && f.OS == "linux":
		return true
	case f.Arch == "amd64" && f.Kind == "installer":
		switch f.OS {
		case "windows":
			return true
		case "darwin":
			if !strings.Contains(f.Filename, "osx10.6") {
				return true
			}
		}
	}
	return false
}

func (f File) URL() string {
	return downloadBaseURL + f.Filename
}

type Release struct {
	Version        string `json:"version"`
	Stable         bool   `json:"stable"`
	Files          []File `json:"files"`
	Visible        bool   `json:"-"` // show files on page load
	SplitPortTable bool   `json:"-"` // whether files should be split by primary/other ports.
}

type Feature struct {
	// The File field will be filled in by the first stable File
	// whose name matches the given fileRE.
	File
	fileRE *regexp.Regexp

	Platform     string // "Microsoft Windows", "Apple macOS", "Linux"
	Requirements string // "Windows XP and above, 64-bit Intel Processor"
}

// featuredFiles lists the platforms and files to be featured
// at the top of the downloads page.
var featuredFiles = []Feature{
	{
		Platform:     "Microsoft Windows",
		Requirements: "Windows 7 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.windows-amd64\.msi$`),
	},
	{
		Platform:     "Apple macOS",
		Requirements: "macOS 10.11 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.darwin-amd64(-osx10\.8)?\.pkg$`),
	},
	{
		Platform:     "Linux",
		Requirements: "Linux 2.6.23 or later, Intel 64-bit processor",
		fileRE:       regexp.MustCompile(`\.linux-amd64\.tar\.gz$`),
	},
	{
		Platform: "Source",
		fileRE:   regexp.MustCompile(`\.src\.tar\.gz$`),
	},
}

// data to send to the template; increment cacheKey if you change this.
type listTemplateData struct {
	Featured                  []Feature
	Stable, Unstable, Archive []Release
	GoogleCN                  bool
}

var (
	listTemplate  = template.Must(template.New("").Funcs(templateFuncs).Parse(templateHTML))
	templateFuncs = template.FuncMap{"pretty": pretty}
)

func filesToFeatured(fs []File) (featured []Feature) {
	for _, feature := range featuredFiles {
		for _, file := range fs {
			if feature.fileRE.MatchString(file.Filename) {
				feature.File = file
				featured = append(featured, feature)
				break
			}
		}
	}
	return
}

func filesToReleases(fs []File) (stable, unstable, archive []Release) {
	sort.Sort(fileOrder(fs))

	var r *Release
	var stableMaj, stableMin int
	add := func() {
		if r == nil {
			return
		}
		if !r.Stable {
			if len(unstable) != 0 {
				// Only show one (latest) unstable version.
				return
			}
			maj, min, _ := parseVersion(r.Version)
			if maj < stableMaj || maj == stableMaj && min <= stableMin {
				// Display unstable version only if newer than the
				// latest stable release.
				return
			}
			unstable = append(unstable, *r)
			return
		}

		// Reports whether the release is the most recent minor version of the
		// two most recent major versions.
		shouldAddStable := func() bool {
			if len(stable) >= 2 {
				// Show up to two stable versions.
				return false
			}
			if len(stable) == 0 {
				// Most recent stable version.
				stableMaj, stableMin, _ = parseVersion(r.Version)
				return true
			}
			if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
				// Older minor version of most recent major version.
				return false
			}
			// Second most recent stable version.
			return true
		}
		if !shouldAddStable() {
			archive = append(archive, *r)
			return
		}

		// Split the file list into primary/other ports for the stable releases.
		// NOTE(cbro): This is only done for stable releases because maintaining the historical
		// nature of primary/other ports for older versions is infeasible.
		// If freebsd is considered primary some time in the future, we'd not want to
		// mark all of the older freebsd binaries as "primary".
		// It might be better if we set that as a flag when uploading.
		r.SplitPortTable = true
		r.Visible = true // Toggle open all stable releases.
		stable = append(stable, *r)
	}
	for _, f := range fs {
		if r == nil || f.Version != r.Version {
			add()
			r = &Release{
				Version: f.Version,
				Stable:  isStable(f.Version),
			}
		}
		r.Files = append(r.Files, f)
	}
	add()
	return
}

// isStable reports whether the version string v is a stable version.
func isStable(v string) bool {
	return !strings.Contains(v, "beta") && !strings.Contains(v, "rc")
}

type fileOrder []File

func (s fileOrder) Len() int      { return len(s) }
func (s fileOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s fileOrder) Less(i, j int) bool {
	a, b := s[i], s[j]
	if av, bv := a.Version, b.Version; av != bv {
		return versionLess(av, bv)
	}
	if a.OS != b.OS {
		return a.OS < b.OS
	}
	if a.Arch != b.Arch {
		return a.Arch < b.Arch
	}
	if a.Kind != b.Kind {
		return a.Kind < b.Kind
	}
	return a.Filename < b.Filename
}

func versionLess(a, b string) bool {
	// Put stable releases first.
	if isStable(a) != isStable(b) {
		return isStable(a)
	}
	maja, mina, ta := parseVersion(a)
	majb, minb, tb := parseVersion(b)
	if maja == majb {
		if mina == minb {
			return ta >= tb
		}
		return mina >= minb
	}
	return maja >= majb
}

func parseVersion(v string) (maj, min int, tail string) {
	if i := strings.Index(v, "beta"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	if i := strings.Index(v, "rc"); i > 0 {
		tail = v[i:]
		v = v[:i]
	}
	p := strings.Split(strings.TrimPrefix(v, "go1."), ".")
	maj, _ = strconv.Atoi(p[0])
	if len(p) < 2 {
		return
	}
	min, _ = strconv.Atoi(p[1])
	return
}

func validUser(user string) bool {
	switch user {
	case "adg", "bradfitz", "cbro", "andybons", "valsorda", "dmitshur", "katiehockman", "julieqiu", "rakoczy":
		return true
	}
	return false
}

var (
	fileRe  = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+\.(tar\.gz|pkg|msi|zip)$`)
	goGetRe = regexp.MustCompile(`^go[0-9a-z.]+\.[0-9a-z.-]+$`)
)

// pretty returns a human-readable version of the given OS, Arch, or Kind.
func pretty(s string) string {
	t, ok := prettyStrings[s]
	if !ok {
		return s
	}
	return t
}

var prettyStrings = map[string]string{
	"darwin":  "macOS",
	"freebsd": "FreeBSD",
	"linux":   "Linux",
	"windows": "Windows",

	"386":    "x86",
	"amd64":  "x86-64",
	"armv6l": "ARMv6",
	"arm64":  "ARMv8",

	"archive":   "Archive",
	"installer": "Installer",
	"source":    "Source",
}
<|endoftext|>


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

// Package internal contains support packages for oauth2 package.
package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

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

// Token represents the crendentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
// This type is a mirror of oauth2.Token and exists to break
// an otherwise-circular dependency. Other internal packages
// should convert this Token into an oauth2.Token before use.
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string

	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string

	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string

	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time

	// Raw optionally contains extra metadata from the server
	// when updating a token.
	Raw interface{}
}

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://www.googleapis.com/",
	"https://github.com/",
	"https://api.instagram.com/",
	"https://www.douban.com/",
	"https://api.dropbox.com/",
	"https://api.soundcloud.com/",
	"https://www.linkedin.com/",
	"https://api.twitch.tv/",
	"https://oauth.vk.com/",
	"https://api.odnoklassniki.ru/",
	"https://connect.stripe.com/",
	"https://api.pushbullet.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://www.strava.com/oauth/",
	"https://app.box.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://user.gini.net/",
	"https://api.netatmo.net/",
	"https://login.mailchimp.com/",
	"https://api.createsend.com/",
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
	hc, err := ContextClient(ctx)
	if err != nil {
		return nil, err
	}
	v.Set("client_id", ClientID)
	bustedAuth := !providerAuthHeaderWorks(TokenURL)
	if bustedAuth && ClientSecret != "" {
		v.Set("client_secret", ClientSecret)
	}
	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(ClientID, ClientSecret)
	}
	r, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	return token, nil
}
<commit_msg>Add disqus oauth exception<commit_after>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package internal contains support packages for oauth2 package.
package internal

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

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

// Token represents the crendentials used to authorize
// the requests to access protected resources on the OAuth 2.0
// provider's backend.
//
// This type is a mirror of oauth2.Token and exists to break
// an otherwise-circular dependency. Other internal packages
// should convert this Token into an oauth2.Token before use.
type Token struct {
	// AccessToken is the token that authorizes and authenticates
	// the requests.
	AccessToken string

	// TokenType is the type of token.
	// The Type method returns either this or "Bearer", the default.
	TokenType string

	// RefreshToken is a token that's used by the application
	// (as opposed to the user) to refresh the access token
	// if it expires.
	RefreshToken string

	// Expiry is the optional expiration time of the access token.
	//
	// If zero, TokenSource implementations will reuse the same
	// token forever and RefreshToken or equivalent
	// mechanisms for that TokenSource will not be used.
	Expiry time.Time

	// Raw optionally contains extra metadata from the server
	// when updating a token.
	Raw interface{}
}

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://www.googleapis.com/",
	"https://github.com/",
	"https://api.instagram.com/",
	"https://www.douban.com/",
	"https://api.dropbox.com/",
	"https://api.soundcloud.com/",
	"https://www.linkedin.com/",
	"https://api.twitch.tv/",
	"https://oauth.vk.com/",
	"https://api.odnoklassniki.ru/",
	"https://connect.stripe.com/",
	"https://api.pushbullet.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://www.strava.com/oauth/",
	"https://app.box.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://user.gini.net/",
	"https://api.netatmo.net/",
	"https://login.mailchimp.com/",
	"https://api.createsend.com/",
	"https://disqus.com/",
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) {
	hc, err := ContextClient(ctx)
	if err != nil {
		return nil, err
	}
	v.Set("client_id", ClientID)
	bustedAuth := !providerAuthHeaderWorks(TokenURL)
	if bustedAuth && ClientSecret != "" {
		v.Set("client_secret", ClientSecret)
	}
	req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(ClientID, ClientSecret)
	}
	r, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	return token, nil
}
<|endoftext|>