<commit_before>package tmpl

import (
	"bytes"
	"html"
	"html/template"
	"strings"
	"time"

	"strconv"

	"fmt"

	"github.com/kennygrant/sanitize"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/util"
)

var funcMap = template.FuncMap{
	"today":                  today,
	"todayCiti":              todayCiti,
	"brdate":                 brDate,
	"replace":                replace,
	"docType":                docType,
	"trim":                   trim,
	"padLeft":                padLeft,
	"clearString":            clearString,
	"toString":               toString,
	"toString64":             toString64,
	"fmtDigitableLine":       fmtDigitableLine,
	"fmtCNPJ":                fmtCNPJ,
	"fmtCPF":                 fmtCPF,
	"fmtDoc":                 fmtDoc,
	"truncate":               truncateString,
	"fmtNumber":              fmtNumber,
	"joinSpace":              joinSpace,
	"brDateWithoutDelimiter": brDateWithoutDelimiter,
	"enDateWithoutDelimiter": enDateWithoutDelimiter,
	"fullDate":               fulldate,
	"enDate":                 enDate,
	"hasErrorTags":           hasErrorTags,
	"toFloatStr":             toFloatStr,
	"concat":                 concat,
	"base64":                 base64,
	"unscape":                unscape,
	"unescapeHtmlString":     unescapeHtmlString,
	"trimLeft":               trimLeft,
	"santanderNSUPrefix":     santanderNSUPrefix,
	"santanderEnv":           santanderEnv,
	"formatSingleLine":       formatSingleLine,
	"diff":                   diff,
	"mod11dv":                calculateOurNumberMod11,
	"mod10dv":                mod10Itau,
	"printIfNotProduction":   printIfNotProduction,
	"itauEnv":                itauEnv,
}

func GetFuncMaps() template.FuncMap {
	return funcMap
}

func santanderNSUPrefix(number string) string {
	if config.Get().DevMode {
		return "TST" + number
	}
	return number
}

func envItau() int {
	if config.Get().DevMode {
		return 1
	}
	return 2
}

func diff(a string, b string) bool {
	return a != b
}

func formatSingleLine(s string) string {
	s1 := strings.Replace(s, "\r", "", -1)
	return strings.Replace(s1, "\n", "; ", -1)
}

func santanderEnv() string {
	if config.Get().DevMode {
		return "T"
	}
	return "P"
}

func padLeft(value, char string, total uint) string {
	s := util.PadLeft(value, char, total)
	return s
}
func unscape(s string) template.HTML {
	return template.HTML(s)
}

func sanitizeHtmlString(s string) string {
	str := html.UnescapeString(s)
	return sanitize.HTML(str)
}

func unescapeHtmlString(s string) template.HTML {
	c := sanitizeHtmlString(s)
	return template.HTML(html.UnescapeString(c))
}

func trimLeft(s string, caract string) string {
	return strings.TrimLeft(s, caract)
}

func truncateString(str string, num int) string {
	bnoden := str
	if len(str) > num {
		bnoden = str[0:num]
	}
	return bnoden
}

func clearString(str string) string {
	s := sanitize.Accents(str)
	var buffer bytes.Buffer
	for _, ch := range s {
		if ch <= 122 && ch >= 32 {
			buffer.WriteString(string(ch))
		}
	}
	return buffer.String()
}

func joinSpace(str ...string) string {
	return strings.Join(str, " ")
}

func hasErrorTags(mapValues map[string]string, errorTags ...string) bool {
	hasError := false
	for _, v := range errorTags {
		if value, exist := mapValues[v]; exist && strings.Trim(value, " ") != "" {
			hasError = true
			break
		}
	}
	return hasError
}

func fmtNumber(n uint64) string {
	real := n / 100
	cents := n % 100
	return fmt.Sprintf("%d,%02d", real, cents)
}

func printIfNotProduction(obj string) string {
	if config.IsNotProduction() {
		return fmt.Sprintf("%s", obj)
	}
	return ""
}

func toFloatStr(n uint64) string {
	real := n / 100
	cents := n % 100
	return fmt.Sprintf("%d.%02d", real, cents)
}

func fmtDoc(doc models.Document) string {
	if e := doc.ValidateCPF(); e == nil {
		return fmtCPF(doc.Number)
	}
	return fmtCNPJ(doc.Number)
}

func toString(number uint) string {
	return strconv.FormatInt(int64(number), 10)
}

func toString64(number uint64) string {
	return strconv.FormatInt(int64(number), 10)
}

func today() time.Time {
	return util.BrNow()
}

func todayCiti() time.Time {
	return util.NycNow()
}

func fulldate(t time.Time) string {
	return t.Format("20060102150405")
}

func brDate(d time.Time) string {
	return d.Format("02/01/2006")
}

func enDate(d time.Time, del string) string {
	return d.Format("2006" + del + "01" + del + "02")
}

func brDateWithoutDelimiter(d time.Time) string {
	return d.Format("02012006")
}

func enDateWithoutDelimiter(d time.Time) string {
	return d.Format("20060102")
}

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func docType(s models.Document) int {
	if s.IsCPF() {
		return 1
	}
	return 2
}

func trim(s string) string {
	return strings.TrimSpace(s)
}
func fmtDigitableLine(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 5 || idx == 15 || idx == 26 {
			buf.WriteString(".")
		}
		if idx == 10 || idx == 21 || idx == 32 || idx == 33 {
			buf.WriteString(" ")
		}
		buf.WriteByte(byte(c))
	}
	return buf.String()
}

func fmtCNPJ(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 2 || idx == 5 {
			buf.WriteString(".")
		}
		if idx == 8 {
			buf.WriteString("/")
		}
		if idx == 12 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}

func fmtCPF(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 3 || idx == 6 {
			buf.WriteString(".")
		}
		if idx == 9 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}

func concat(s ...string) string {
	buf := bytes.Buffer{}
	for _, item := range s {
		buf.WriteString(item)
	}
	return buf.String()
}

func base64(s string) string {
	return util.Base64(s)
}

func calculateOurNumberMod11(number uint) uint {
	ourNumberWithDigit := strconv.Itoa(int(number)) + util.OurNumberDv(strconv.Itoa(int(number)), util.MOD11)
	value, _ := strconv.Atoi(ourNumberWithDigit)
	return uint(value)
}

func mod10Itau(number string, agency string, account string, wallet uint16) string {

	var buffer bytes.Buffer

	if wallet == 126 || wallet == 131 || wallet == 146 || wallet == 168 {

		buffer.WriteString(strconv.FormatUint(uint64(wallet), 10))
		buffer.WriteString(number)

		return util.OurNumberDv(buffer.String(), util.MOD10)
	} else {
		buffer.WriteString(agency)
		buffer.WriteString(account)
		buffer.WriteString(strconv.FormatUint(uint64(wallet), 10))
		buffer.WriteString(number)
		return util.OurNumberDv(buffer.String(), util.MOD10)
	}
}

func itauEnv() string {
	if config.Get().DevMode {
		return "1"
	}
	return "2"
}
<commit_msg>:art: merge remaining<commit_after>package tmpl

import (
	"bytes"
	"html"
	"html/template"
	"strings"
	"time"

	"strconv"

	"fmt"

	"github.com/kennygrant/sanitize"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/util"
)

var funcMap = template.FuncMap{
	"today":                  today,
	"todayCiti":              todayCiti,
	"brdate":                 brDate,
	"replace":                replace,
	"docType":                docType,
	"trim":                   trim,
	"padLeft":                padLeft,
	"clearString":            clearString,
	"toString":               toString,
	"toString64":             toString64,
	"fmtDigitableLine":       fmtDigitableLine,
	"fmtCNPJ":                fmtCNPJ,
	"fmtCPF":                 fmtCPF,
	"fmtDoc":                 fmtDoc,
	"truncate":               truncateString,
	"fmtNumber":              fmtNumber,
	"joinSpace":              joinSpace,
	"brDateWithoutDelimiter": brDateWithoutDelimiter,
	"enDateWithoutDelimiter": enDateWithoutDelimiter,
	"fullDate":               fulldate,
	"enDate":                 enDate,
	"hasErrorTags":           hasErrorTags,
	"toFloatStr":             toFloatStr,
	"concat":                 concat,
	"base64":                 base64,
	"unscape":                unscape,
	"unescapeHtmlString":     unescapeHtmlString,
	"trimLeft":               trimLeft,
	"santanderNSUPrefix":     santanderNSUPrefix,
	"santanderEnv":           santanderEnv,
	"formatSingleLine":       formatSingleLine,
	"diff":                   diff,
	"mod11dv":                calculateOurNumberMod11,
	"mod10dv":                mod10Itau,
	"printIfNotProduction":   printIfNotProduction,
	"itauEnv":                itauEnv,
}

func GetFuncMaps() template.FuncMap {
	return funcMap
}

func santanderNSUPrefix(number string) string {
	if config.Get().DevMode {
		return "TST" + number
	}
	return number
}

func diff(a string, b string) bool {
	return a != b
}

func formatSingleLine(s string) string {
	s1 := strings.Replace(s, "\r", "", -1)
	return strings.Replace(s1, "\n", "; ", -1)
}

func santanderEnv() string {
	if config.Get().DevMode {
		return "T"
	}
	return "P"
}

func padLeft(value, char string, total uint) string {
	s := util.PadLeft(value, char, total)
	return s
}
func unscape(s string) template.HTML {
	return template.HTML(s)
}

func sanitizeHtmlString(s string) string {
	str := html.UnescapeString(s)
	return sanitize.HTML(str)
}

func unescapeHtmlString(s string) template.HTML {
	c := sanitizeHtmlString(s)
	return template.HTML(html.UnescapeString(c))
}

func trimLeft(s string, caract string) string {
	return strings.TrimLeft(s, caract)
}

func truncateString(str string, num int) string {
	bnoden := str
	if len(str) > num {
		bnoden = str[0:num]
	}
	return bnoden
}

func clearString(str string) string {
	s := sanitize.Accents(str)
	var buffer bytes.Buffer
	for _, ch := range s {
		if ch <= 122 && ch >= 32 {
			buffer.WriteString(string(ch))
		}
	}
	return buffer.String()
}

func joinSpace(str ...string) string {
	return strings.Join(str, " ")
}

func hasErrorTags(mapValues map[string]string, errorTags ...string) bool {
	hasError := false
	for _, v := range errorTags {
		if value, exist := mapValues[v]; exist && strings.Trim(value, " ") != "" {
			hasError = true
			break
		}
	}
	return hasError
}

func fmtNumber(n uint64) string {
	real := n / 100
	cents := n % 100
	return fmt.Sprintf("%d,%02d", real, cents)
}

func printIfNotProduction(obj string) string {
	if config.IsNotProduction() {
		return fmt.Sprintf("%s", obj)
	}
	return ""
}

func toFloatStr(n uint64) string {
	real := n / 100
	cents := n % 100
	return fmt.Sprintf("%d.%02d", real, cents)
}

func fmtDoc(doc models.Document) string {
	if e := doc.ValidateCPF(); e == nil {
		return fmtCPF(doc.Number)
	}
	return fmtCNPJ(doc.Number)
}

func toString(number uint) string {
	return strconv.FormatInt(int64(number), 10)
}

func toString64(number uint64) string {
	return strconv.FormatInt(int64(number), 10)
}

func today() time.Time {
	return util.BrNow()
}

func todayCiti() time.Time {
	return util.NycNow()
}

func fulldate(t time.Time) string {
	return t.Format("20060102150405")
}

func brDate(d time.Time) string {
	return d.Format("02/01/2006")
}

func enDate(d time.Time, del string) string {
	return d.Format("2006" + del + "01" + del + "02")
}

func brDateWithoutDelimiter(d time.Time) string {
	return d.Format("02012006")
}

func enDateWithoutDelimiter(d time.Time) string {
	return d.Format("20060102")
}

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func docType(s models.Document) int {
	if s.IsCPF() {
		return 1
	}
	return 2
}

func trim(s string) string {
	return strings.TrimSpace(s)
}
func fmtDigitableLine(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 5 || idx == 15 || idx == 26 {
			buf.WriteString(".")
		}
		if idx == 10 || idx == 21 || idx == 32 || idx == 33 {
			buf.WriteString(" ")
		}
		buf.WriteByte(byte(c))
	}
	return buf.String()
}

func fmtCNPJ(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 2 || idx == 5 {
			buf.WriteString(".")
		}
		if idx == 8 {
			buf.WriteString("/")
		}
		if idx == 12 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}

func fmtCPF(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 3 || idx == 6 {
			buf.WriteString(".")
		}
		if idx == 9 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}

func concat(s ...string) string {
	buf := bytes.Buffer{}
	for _, item := range s {
		buf.WriteString(item)
	}
	return buf.String()
}

func base64(s string) string {
	return util.Base64(s)
}

func calculateOurNumberMod11(number uint) uint {
	ourNumberWithDigit := strconv.Itoa(int(number)) + util.OurNumberDv(strconv.Itoa(int(number)), util.MOD11)
	value, _ := strconv.Atoi(ourNumberWithDigit)
	return uint(value)
}

func mod10Itau(number string, agency string, account string, wallet uint16) string {

	var buffer bytes.Buffer

	if wallet == 126 || wallet == 131 || wallet == 146 || wallet == 168 {

		buffer.WriteString(strconv.FormatUint(uint64(wallet), 10))
		buffer.WriteString(number)

		return util.OurNumberDv(buffer.String(), util.MOD10)
	} else {
		buffer.WriteString(agency)
		buffer.WriteString(account)
		buffer.WriteString(strconv.FormatUint(uint64(wallet), 10))
		buffer.WriteString(number)
		return util.OurNumberDv(buffer.String(), util.MOD10)
	}
}

func itauEnv() string {
	if config.Get().DevMode {
		return "1"
	}
	return "2"
}
<|endoftext|>


<commit_before>package jsonschema

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// Using a pointer allows us to handle recursive embedded schemas.
type EmbeddedSchemas map[string]*Schema

func (e *EmbeddedSchemas) UnmarshalJSON(b []byte) error {
	*e = make(EmbeddedSchemas)
	err1 := e.UnmarshalArray(b)
	err2 := e.UnmarshalObject(b)
	err3 := e.UnmarshalSingle(b)
	if err1 != nil && err2 != nil && err3 != nil {
		return errors.New("no valid embedded schemas")
	}
	return nil
}

func (e *EmbeddedSchemas) UnmarshalArray(b []byte) error {
	var schemas []*Schema
	if err := json.Unmarshal(b, &schemas); err != nil {
		return err
	}
	for i, v := range schemas {
		(*e)[strconv.Itoa(i)] = v
	}
	return nil
}

func (e *EmbeddedSchemas) UnmarshalObject(b []byte) error {
	var m map[string]*Schema
	if err := json.Unmarshal(b, &m); err != nil {
		return err
	}
	for k, v := range m {
		(*e)[k] = v
	}
	return nil
}

func (e *EmbeddedSchemas) UnmarshalSingle(b []byte) error {
	var s Schema
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	(*e)[""] = &s
	return nil
}

// resolveRefs starts a depth-first search through a document for schemas containing
// the 'ref' validator. It completely resolves each one found.
func (s *Schema) resolveRefs(loadExternal bool) {
	s.resolveSelfAndBelow(*s, *s, loadExternal)
}

func (s *Schema) resolveSelfAndBelow(parentSchema, rootSchema Schema, loadExternal bool) {
	if parentSchema.id != "" && parentSchema.id != s.id {
		s.parentId = parentSchema.id
		sURL, sURLErr := url.Parse(s.id)
		pURL, pURLErr := url.Parse(parentSchema.id)
		if sURLErr == nil && pURLErr == nil && pURL.IsAbs() && !sURL.IsAbs() {
			if strings.HasPrefix(s.id, "#") {
				pURL.Fragment = strings.TrimPrefix(s.id, "#")
			} else {
				pURL.Path = s.id
				s.id = pURL.String()
			}
		}
	}
	s.resolveSelf(rootSchema, loadExternal)
	s.resolveBelow(rootSchema, loadExternal)
}

func (s *Schema) resolveSelf(rootSchema Schema, loadExternal bool) {
	if str, ok := s.hasRef(); ok {
		sch, err := s.refToSchema(str, rootSchema, loadExternal)
		if err != nil {
			return
		}
		*s = *sch
		s.resolveSelf(rootSchema, loadExternal)
	}
}

// TODO: test that we fail gracefully if the schema contains infinitely looping "$ref"s.
func (s *Schema) resolveBelow(rootSchema Schema, loadExternal bool) {
	if s.resolved == true {
		return
	}
	s.resolved = true
	for _, n := range s.nodes {
		for _, sch := range n.EmbeddedSchemas {
			sch.resolveSelfAndBelow(*s, rootSchema, loadExternal)
		}
	}
}

func (s *Schema) hasRef() (string, bool) {
	for _, n := range s.nodes {
		if r, ok := n.Validator.(*ref); ok {
			return string(*r), true
		}
	}
	return "", false
}

// TODO: This is hacky. Look into using a library like gojsonpointer[1] instead.
//
// [1] https://github.com/xeipuuv/gojsonpointer
func (s *Schema) refToSchema(str string, rootSchema Schema, loadExternal bool) (*Schema, error) {
	parentURL, err := url.Parse(s.parentId)
	if err == nil && parentURL.IsAbs() {
		sURL, err := url.Parse(str)
		if err == nil && !sURL.IsAbs() && !strings.HasPrefix(str, "#") {
			str = s.parentId + str
		}
	}

	var split []string
	url, err := url.Parse(str)
	if err == nil && url.IsAbs() {
		cacheKey := strings.TrimSuffix(str, url.Fragment)
		cachedSchema, ok := rootSchema.Cache[cacheKey]
		if ok {
			rootSchema = *cachedSchema
		} else {
			// Handle external URIs.
			if !loadExternal {
				return new(Schema), errors.New("external schemas are disabled")
			}
			resp, err := http.Get(str)
			if err != nil {
				return new(Schema), errors.New("bad external url")
			}
			defer resp.Body.Close()
			s, err := ParseWithCache(resp.Body, loadExternal, &rootSchema.Cache)
			if err != nil {
				return new(Schema), errors.New("error parsing external doc")
			}
			rootSchema.Cache[cacheKey] = s
			rootSchema = *s
		}
		str = url.Fragment
	}

	// Remove the prefix from internal URIs.
	str = strings.TrimPrefix(str, "#")
	str = strings.TrimPrefix(str, "/")

	split = strings.Split(str, "/")
	// Make replacements.
	for i, v := range split {
		r := strings.NewReplacer("~0", "~", "~1", "/", "%25", "%")
		split[i] = r.Replace(v)
	}
	// Resolve the local part of the URI.
	return resolveLocalPath(split, rootSchema, str)
}

// TODO: add code and tests for references more than one level deep.
func resolveLocalPath(split []string, rootSchema Schema, str string) (*Schema, error) {
	switch len(split) {
	case 1:
		if split[0] == "" {
			return &rootSchema, nil
		}
		v, ok := rootSchema.nodes[split[0]]
		if ok == false {
			break
		}
		if s, ok := v.EmbeddedSchemas[""]; ok {
			return s, nil
		}
	case 2:
		v, ok := rootSchema.nodes[split[0]]
		if ok == false {
			break
		}
		if s, ok := v.EmbeddedSchemas[split[1]]; ok {
			return s, nil
		}
	}
	return new(Schema), fmt.Errorf("failed to resolve %s", str)
}
<commit_msg>Ensure schemas with absolute ids are cached<commit_after>package jsonschema

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// Using a pointer allows us to handle recursive embedded schemas.
type EmbeddedSchemas map[string]*Schema

func (e *EmbeddedSchemas) UnmarshalJSON(b []byte) error {
	*e = make(EmbeddedSchemas)
	err1 := e.UnmarshalArray(b)
	err2 := e.UnmarshalObject(b)
	err3 := e.UnmarshalSingle(b)
	if err1 != nil && err2 != nil && err3 != nil {
		return errors.New("no valid embedded schemas")
	}
	return nil
}

func (e *EmbeddedSchemas) UnmarshalArray(b []byte) error {
	var schemas []*Schema
	if err := json.Unmarshal(b, &schemas); err != nil {
		return err
	}
	for i, v := range schemas {
		(*e)[strconv.Itoa(i)] = v
	}
	return nil
}

func (e *EmbeddedSchemas) UnmarshalObject(b []byte) error {
	var m map[string]*Schema
	if err := json.Unmarshal(b, &m); err != nil {
		return err
	}
	for k, v := range m {
		(*e)[k] = v
	}
	return nil
}

func (e *EmbeddedSchemas) UnmarshalSingle(b []byte) error {
	var s Schema
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	(*e)[""] = &s
	return nil
}

// resolveRefs starts a depth-first search through a document for schemas containing
// the 'ref' validator. It completely resolves each one found.
func (s *Schema) resolveRefs(loadExternal bool) {
	s.resolveSelfAndBelow(*s, *s, loadExternal)
}

func (s *Schema) resolveSelfAndBelow(parentSchema, rootSchema Schema, loadExternal bool) {
	if parentSchema.id != "" && parentSchema.id != s.id {
		s.parentId = parentSchema.id
		sURL, sURLErr := url.Parse(s.id)
		pURL, pURLErr := url.Parse(parentSchema.id)
		if sURLErr == nil && pURLErr == nil && pURL.IsAbs() && !sURL.IsAbs() {
			if strings.HasPrefix(s.id, "#") {
				pURL.Fragment = strings.TrimPrefix(s.id, "#")
			} else {
				pURL.Path = s.id
				s.id = pURL.String()
			}
		}
	}
	cacheKey, err := resolveCacheKey(s.id)
	if err == nil {
		if _, ok := rootSchema.Cache[cacheKey]; !ok {
			rootSchema.Cache[cacheKey] = s
		}
	}
	s.resolveSelf(rootSchema, loadExternal)
	s.resolveBelow(rootSchema, loadExternal)
}

func (s *Schema) resolveSelf(rootSchema Schema, loadExternal bool) {
	if str, ok := s.hasRef(); ok {
		sch, err := s.refToSchema(str, rootSchema, loadExternal)
		if err != nil {
			return
		}
		*s = *sch
		s.resolveSelf(rootSchema, loadExternal)
	}
}

// TODO: test that we fail gracefully if the schema contains infinitely looping "$ref"s.
func (s *Schema) resolveBelow(rootSchema Schema, loadExternal bool) {
	if s.resolved == true {
		return
	}
	s.resolved = true
	for _, n := range s.nodes {
		for _, sch := range n.EmbeddedSchemas {
			sch.resolveSelfAndBelow(*s, rootSchema, loadExternal)
		}
	}
}

func (s *Schema) hasRef() (string, bool) {
	for _, n := range s.nodes {
		if r, ok := n.Validator.(*ref); ok {
			return string(*r), true
		}
	}
	return "", false
}

// TODO: This is hacky. Look into using a library like gojsonpointer[1] instead.
//
// [1] https://github.com/xeipuuv/gojsonpointer
func (s *Schema) refToSchema(str string, rootSchema Schema, loadExternal bool) (*Schema, error) {
	parentURL, err := url.Parse(s.parentId)
	if err == nil && parentURL.IsAbs() {
		sURL, err := url.Parse(str)
		if err == nil && !sURL.IsAbs() && !strings.HasPrefix(str, "#") {
			str = s.parentId + str
		}
	}

	var split []string
	url, err := url.Parse(str)
	cacheKey, cacheKeyErr := resolveCacheKey(str)
	if err == nil && cacheKeyErr == nil {
		cachedSchema, ok := rootSchema.Cache[cacheKey]
		if ok {
			rootSchema = *cachedSchema
		} else {
			// Handle external URIs.
			if !loadExternal {
				return new(Schema), errors.New("external schemas are disabled")
			}
			resp, err := http.Get(str)
			if err != nil {
				return new(Schema), errors.New("bad external url")
			}
			defer resp.Body.Close()
			s, err := ParseWithCache(resp.Body, loadExternal, &rootSchema.Cache)
			if err != nil {
				return new(Schema), errors.New("error parsing external doc")
			}
			rootSchema.Cache[cacheKey] = s
			rootSchema = *s
		}
		str = url.Fragment
	}

	// Remove the prefix from internal URIs.
	str = strings.TrimPrefix(str, "#")
	str = strings.TrimPrefix(str, "/")

	split = strings.Split(str, "/")
	// Make replacements.
	for i, v := range split {
		r := strings.NewReplacer("~0", "~", "~1", "/", "%25", "%")
		split[i] = r.Replace(v)
	}
	// Resolve the local part of the URI.
	return resolveLocalPath(split, rootSchema, str)
}

// TODO: add code and tests for references more than one level deep.
func resolveLocalPath(split []string, rootSchema Schema, str string) (*Schema, error) {
	switch len(split) {
	case 1:
		if split[0] == "" {
			return &rootSchema, nil
		}
		v, ok := rootSchema.nodes[split[0]]
		if ok == false {
			break
		}
		if s, ok := v.EmbeddedSchemas[""]; ok {
			return s, nil
		}
	case 2:
		v, ok := rootSchema.nodes[split[0]]
		if ok == false {
			break
		}
		if s, ok := v.EmbeddedSchemas[split[1]]; ok {
			return s, nil
		}
	}
	return new(Schema), fmt.Errorf("failed to resolve %s", str)
}

func resolveCacheKey(id string) (string, error) {
	url, err := url.Parse(id)
	if err == nil && url.IsAbs() {
		url.Fragment = ""
		return strings.TrimSuffix(url.String(), "/"), nil
	}
	return "", fmt.Errorf("failed to resolve cache key for %s", id)
}
<|endoftext|>


<commit_before>package storage

import (
	"path"

	"github.com/Lunchr/luncher-api/db/model"
	"github.com/deiwin/imstor"
)

// Images is a wrapper around imstor.Storage that offers a type safe way
// to get paths for image sizes specific to this application. It also prepends
// the `images/` to all the returned paths.
type Images interface {
	ChecksumDataURL(string) (string, error)
	StoreDataURL(string) error
	PathsFor(checksum string) (*model.OfferImagePaths, error)
}

type images struct {
	imstor.Storage
}

const (
	large     = "large"
	thumbnail = "thumbnail"
)

func NewImages() Images {
	// We don't really care about the widths of the images, but double the height
	// seems like a reasonable limit
	sizes := []imstor.Size{
		imstor.Size{
			Name:   large,
			Width:  2 * 400,
			Height: 400,
		},
		imstor.Size{
			Name:   thumbnail,
			Width:  2 * 60,
			Height: 60,
		},
	}
	formats := []imstor.Format{
		imstor.PNG2JPEG,
		imstor.JPEGFormat,
	}
	conf := imstor.NewConfig(sizes, formats)
	return images{
		imstor.New(conf),
	}
}

// PathsFor returns a struct holding the image paths to the various sizes of images
// stored with the provided checksum. Returns nil for an empty checksum.
func (i images) PathsFor(checksum string) (*model.OfferImagePaths, error) {
	if checksum == "" {
		return nil, nil
	}
	largePath, err := i.pathForLarge(checksum)
	if err != nil {
		return nil, err
	}
	thumbnailPath, err := i.pathForThumbnail(checksum)
	if err != nil {
		return nil, err
	}
	return &model.OfferImagePaths{
		Large:     largePath,
		Thumbnail: thumbnailPath,
	}, nil
}

func (i images) pathForThumbnail(checksum string) (string, error) {
	return i.pathFor(checksum, thumbnail)
}

func (i images) pathForLarge(checksum string) (string, error) {
	return i.pathFor(checksum, large)
}

func (i images) pathFor(checksum, size string) (string, error) {
	p, err := i.PathForSize(checksum, size)
	if err != nil {
		return "", err
	}
	return path.Join("images", p), nil
}
<commit_msg>Add a method to check if an image has already been stored<commit_after>package storage

import (
	"path"

	"github.com/Lunchr/luncher-api/db/model"
	"github.com/deiwin/imstor"
)

// Images is a wrapper around imstor.Storage that offers a type safe way
// to get paths for image sizes specific to this application. It also prepends
// the `images/` to all the returned paths.
type Images interface {
	ChecksumDataURL(string) (string, error)
	StoreDataURL(string) error
	PathsFor(checksum string) (*model.OfferImagePaths, error)
	HasChecksum(checksum string) (bool, error)
}

type images struct {
	imstor.Storage
	sizeStrings []string
}

const (
	large     = "large"
	thumbnail = "thumbnail"
)

func NewImages() Images {
	// We don't really care about the widths of the images, but double the height
	// seems like a reasonable limit
	sizes := []imstor.Size{
		imstor.Size{
			Name:   large,
			Width:  2 * 400,
			Height: 400,
		},
		imstor.Size{
			Name:   thumbnail,
			Width:  2 * 60,
			Height: 60,
		},
	}
	sizeStrings := make([]string, len(sizes))
	for i, size := range sizes {
		sizeStrings[i] = size.Name
	}
	formats := []imstor.Format{
		imstor.PNG2JPEG,
		imstor.JPEGFormat,
	}
	conf := imstor.NewConfig(sizes, formats)
	return images{
		Storage:     imstor.New(conf),
		sizeStrings: sizeStrings,
	}
}

// PathsFor returns a struct holding the image paths to the various sizes of images
// stored with the provided checksum. Returns nil for an empty checksum.
func (i images) PathsFor(checksum string) (*model.OfferImagePaths, error) {
	if checksum == "" {
		return nil, nil
	}
	largePath, err := i.pathForLarge(checksum)
	if err != nil {
		return nil, err
	}
	thumbnailPath, err := i.pathForThumbnail(checksum)
	if err != nil {
		return nil, err
	}
	return &model.OfferImagePaths{
		Large:     largePath,
		Thumbnail: thumbnailPath,
	}, nil
}

func (i images) HasChecksum(checksum string) (bool, error) {
	return i.HasSizesForChecksum(checksum, i.sizeStrings)
}

func (i images) pathForThumbnail(checksum string) (string, error) {
	return i.pathFor(checksum, thumbnail)
}

func (i images) pathForLarge(checksum string) (string, error) {
	return i.pathFor(checksum, large)
}

func (i images) pathFor(checksum, size string) (string, error) {
	p, err := i.PathForSize(checksum, size)
	if err != nil {
		return "", err
	}
	return path.Join("images", p), nil
}
<|endoftext|>


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

package storage

import (
	"math"
	"sort"

	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/tsdb/chunkenc"
	"github.com/prometheus/prometheus/tsdb/chunks"
	"github.com/prometheus/prometheus/tsdb/tsdbutil"
)

type SeriesEntry struct {
	Lset             labels.Labels
	SampleIteratorFn func() chunkenc.Iterator
}

func (s *SeriesEntry) Labels() labels.Labels       { return s.Lset }
func (s *SeriesEntry) Iterator() chunkenc.Iterator { return s.SampleIteratorFn() }

type ChunkSeriesEntry struct {
	Lset            labels.Labels
	ChunkIteratorFn func() chunks.Iterator
}

func (s *ChunkSeriesEntry) Labels() labels.Labels     { return s.Lset }
func (s *ChunkSeriesEntry) Iterator() chunks.Iterator { return s.ChunkIteratorFn() }

// NewListSeries returns series entry with iterator that allows to iterate over provided samples.
func NewListSeries(lset labels.Labels, s []tsdbutil.Sample) *SeriesEntry {
	return &SeriesEntry{
		Lset: lset,
		SampleIteratorFn: func() chunkenc.Iterator {
			return NewListSeriesIterator(samples(s))
		},
	}
}

// NewListChunkSeriesIterator returns chunk series entry that allows to iterate over provided samples.
// NOTE: It uses inefficient chunks encoding implementation, not caring about chunk size.
func NewListChunkSeriesFromSamples(lset labels.Labels, samples ...[]tsdbutil.Sample) *ChunkSeriesEntry {
	return &ChunkSeriesEntry{
		Lset: lset,
		ChunkIteratorFn: func() chunks.Iterator {
			chks := make([]chunks.Meta, 0, len(samples))
			for _, s := range samples {
				chks = append(chks, tsdbutil.ChunkFromSamples(s))
			}
			return NewListChunkSeriesIterator(chks...)
		},
	}
}

type listSeriesIterator struct {
	samples Samples
	idx     int
}

type samples []tsdbutil.Sample

func (s samples) Get(i int) tsdbutil.Sample { return s[i] }
func (s samples) Len() int                  { return len(s) }

// Samples interface allows to work on arrays of types that are compatible with tsdbutil.Sample.
type Samples interface {
	Get(i int) tsdbutil.Sample
	Len() int
}

// NewListSeriesIterator returns listSeriesIterator that allows to iterate over provided samples.
func NewListSeriesIterator(samples Samples) chunkenc.Iterator {
	return &listSeriesIterator{samples: samples, idx: -1}
}

func (it *listSeriesIterator) At() (int64, float64) {
	s := it.samples.Get(it.idx)
	return s.T(), s.V()
}

func (it *listSeriesIterator) Next() bool {
	it.idx++
	return it.idx < it.samples.Len()
}

func (it *listSeriesIterator) Seek(t int64) bool {
	if it.idx == -1 {
		it.idx = 0
	}
	// Do binary search between current position and end.
	it.idx = sort.Search(it.samples.Len()-it.idx, func(i int) bool {
		s := it.samples.Get(i + it.idx)
		return s.T() >= t
	})

	return it.idx < it.samples.Len()
}

func (it *listSeriesIterator) Err() error { return nil }

type listChunkSeriesIterator struct {
	chks []chunks.Meta
	idx  int
}

// NewListChunkSeriesIterator returns listChunkSeriesIterator that allows to iterate over provided chunks.
func NewListChunkSeriesIterator(chks ...chunks.Meta) chunks.Iterator {
	return &listChunkSeriesIterator{chks: chks, idx: -1}
}

func (it *listChunkSeriesIterator) At() chunks.Meta {
	return it.chks[it.idx]
}

func (it *listChunkSeriesIterator) Next() bool {
	it.idx++
	return it.idx < len(it.chks)
}

func (it *listChunkSeriesIterator) Err() error { return nil }

type chunkSetToSeriesSet struct {
	ChunkSeriesSet

	chkIterErr       error
	sameSeriesChunks []Series
}

// NewSeriesSetFromChunkSeriesSet converts ChunkSeriesSet to SeriesSet by decoding chunks one by one.
func NewSeriesSetFromChunkSeriesSet(chk ChunkSeriesSet) SeriesSet {
	return &chunkSetToSeriesSet{ChunkSeriesSet: chk}
}

func (c *chunkSetToSeriesSet) Next() bool {
	if c.Err() != nil || !c.ChunkSeriesSet.Next() {
		return false
	}

	iter := c.ChunkSeriesSet.At().Iterator()
	c.sameSeriesChunks = c.sameSeriesChunks[:0]

	for iter.Next() {
		c.sameSeriesChunks = append(
			c.sameSeriesChunks,
			newChunkToSeriesDecoder(c.ChunkSeriesSet.At().Labels(), iter.At()),
		)
	}

	if iter.Err() != nil {
		c.chkIterErr = iter.Err()
		return false
	}
	return true
}

func (c *chunkSetToSeriesSet) At() Series {
	// Series composed of same chunks for the same series.
	return ChainedSeriesMerge(c.sameSeriesChunks...)
}

func (c *chunkSetToSeriesSet) Err() error {
	if c.chkIterErr != nil {
		return c.chkIterErr
	}
	return c.ChunkSeriesSet.Err()
}

func newChunkToSeriesDecoder(labels labels.Labels, chk chunks.Meta) Series {
	return &SeriesEntry{
		Lset: labels,
		SampleIteratorFn: func() chunkenc.Iterator {
			// TODO(bwplotka): Can we provide any chunkenc buffer?
			return chk.Chunk.Iterator(nil)
		},
	}
}

type seriesSetToChunkSet struct {
	SeriesSet
}

// NewSeriesSetToChunkSet converts SeriesSet to ChunkSeriesSet by encoding chunks from samples.
func NewSeriesSetToChunkSet(chk SeriesSet) ChunkSeriesSet {
	return &seriesSetToChunkSet{SeriesSet: chk}
}

func (c *seriesSetToChunkSet) Next() bool {
	if c.Err() != nil || !c.SeriesSet.Next() {
		return false
	}
	return true
}

func (c *seriesSetToChunkSet) At() ChunkSeries {
	return &seriesToChunkEncoder{
		Series: c.SeriesSet.At(),
	}
}

func (c *seriesSetToChunkSet) Err() error {
	return c.SeriesSet.Err()
}

type seriesToChunkEncoder struct {
	Series
}

// TODO(bwplotka): Currently encoder will just naively build one chunk, without limit. Split it: https://github.com/prometheus/tsdb/issues/670
func (s *seriesToChunkEncoder) Iterator() chunks.Iterator {
	chk := chunkenc.NewXORChunk()
	app, err := chk.Appender()
	if err != nil {
		return errChunksIterator{err: err}
	}
	mint := int64(math.MaxInt64)
	maxt := int64(math.MinInt64)

	seriesIter := s.Series.Iterator()
	for seriesIter.Next() {
		t, v := seriesIter.At()
		app.Append(t, v)

		maxt = t
		if mint == math.MaxInt64 {
			mint = t
		}
	}
	if err := seriesIter.Err(); err != nil {
		return errChunksIterator{err: err}
	}

	return NewListChunkSeriesIterator(chunks.Meta{
		MinTime: mint,
		MaxTime: maxt,
		Chunk:   chk,
	})
}

type errChunksIterator struct {
	err error
}

func (e errChunksIterator) At() chunks.Meta { return chunks.Meta{} }
func (e errChunksIterator) Next() bool      { return false }
func (e errChunksIterator) Err() error      { return e.err }

// ExpandSamples iterates over all samples in the iterator, buffering all in slice.
// Optionally it takes samples constructor, useful when you want to compare sample slices with different
// sample implementations. if nil, sample type from this package will be used.
func ExpandSamples(iter chunkenc.Iterator, newSampleFn func(t int64, v float64) tsdbutil.Sample) ([]tsdbutil.Sample, error) {
	if newSampleFn == nil {
		newSampleFn = func(t int64, v float64) tsdbutil.Sample { return sample{t, v} }
	}

	var result []tsdbutil.Sample
	for iter.Next() {
		t, v := iter.At()
		// NaNs can't be compared normally, so substitute for another value.
		if math.IsNaN(v) {
			v = -42
		}
		result = append(result, newSampleFn(t, v))
	}
	return result, iter.Err()
}

// ExpandChunks iterates over all chunks in the iterator, buffering all in slice.
func ExpandChunks(iter chunks.Iterator) ([]chunks.Meta, error) {
	var result []chunks.Meta
	for iter.Next() {
		result = append(result, iter.At())
	}
	return result, iter.Err()
}
<commit_msg>Fix golint issue caused by incorrect func name (#7756)<commit_after>// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
	"math"
	"sort"

	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/tsdb/chunkenc"
	"github.com/prometheus/prometheus/tsdb/chunks"
	"github.com/prometheus/prometheus/tsdb/tsdbutil"
)

type SeriesEntry struct {
	Lset             labels.Labels
	SampleIteratorFn func() chunkenc.Iterator
}

func (s *SeriesEntry) Labels() labels.Labels       { return s.Lset }
func (s *SeriesEntry) Iterator() chunkenc.Iterator { return s.SampleIteratorFn() }

type ChunkSeriesEntry struct {
	Lset            labels.Labels
	ChunkIteratorFn func() chunks.Iterator
}

func (s *ChunkSeriesEntry) Labels() labels.Labels     { return s.Lset }
func (s *ChunkSeriesEntry) Iterator() chunks.Iterator { return s.ChunkIteratorFn() }

// NewListSeries returns series entry with iterator that allows to iterate over provided samples.
func NewListSeries(lset labels.Labels, s []tsdbutil.Sample) *SeriesEntry {
	return &SeriesEntry{
		Lset: lset,
		SampleIteratorFn: func() chunkenc.Iterator {
			return NewListSeriesIterator(samples(s))
		},
	}
}

// NewListChunkSeriesFromSamples returns chunk series entry that allows to iterate over provided samples.
// NOTE: It uses inefficient chunks encoding implementation, not caring about chunk size.
func NewListChunkSeriesFromSamples(lset labels.Labels, samples ...[]tsdbutil.Sample) *ChunkSeriesEntry {
	return &ChunkSeriesEntry{
		Lset: lset,
		ChunkIteratorFn: func() chunks.Iterator {
			chks := make([]chunks.Meta, 0, len(samples))
			for _, s := range samples {
				chks = append(chks, tsdbutil.ChunkFromSamples(s))
			}
			return NewListChunkSeriesIterator(chks...)
		},
	}
}

type listSeriesIterator struct {
	samples Samples
	idx     int
}

type samples []tsdbutil.Sample

func (s samples) Get(i int) tsdbutil.Sample { return s[i] }
func (s samples) Len() int                  { return len(s) }

// Samples interface allows to work on arrays of types that are compatible with tsdbutil.Sample.
type Samples interface {
	Get(i int) tsdbutil.Sample
	Len() int
}

// NewListSeriesIterator returns listSeriesIterator that allows to iterate over provided samples.
func NewListSeriesIterator(samples Samples) chunkenc.Iterator {
	return &listSeriesIterator{samples: samples, idx: -1}
}

func (it *listSeriesIterator) At() (int64, float64) {
	s := it.samples.Get(it.idx)
	return s.T(), s.V()
}

func (it *listSeriesIterator) Next() bool {
	it.idx++
	return it.idx < it.samples.Len()
}

func (it *listSeriesIterator) Seek(t int64) bool {
	if it.idx == -1 {
		it.idx = 0
	}
	// Do binary search between current position and end.
	it.idx = sort.Search(it.samples.Len()-it.idx, func(i int) bool {
		s := it.samples.Get(i + it.idx)
		return s.T() >= t
	})

	return it.idx < it.samples.Len()
}

func (it *listSeriesIterator) Err() error { return nil }

type listChunkSeriesIterator struct {
	chks []chunks.Meta
	idx  int
}

// NewListChunkSeriesIterator returns listChunkSeriesIterator that allows to iterate over provided chunks.
func NewListChunkSeriesIterator(chks ...chunks.Meta) chunks.Iterator {
	return &listChunkSeriesIterator{chks: chks, idx: -1}
}

func (it *listChunkSeriesIterator) At() chunks.Meta {
	return it.chks[it.idx]
}

func (it *listChunkSeriesIterator) Next() bool {
	it.idx++
	return it.idx < len(it.chks)
}

func (it *listChunkSeriesIterator) Err() error { return nil }

type chunkSetToSeriesSet struct {
	ChunkSeriesSet

	chkIterErr       error
	sameSeriesChunks []Series
}

// NewSeriesSetFromChunkSeriesSet converts ChunkSeriesSet to SeriesSet by decoding chunks one by one.
func NewSeriesSetFromChunkSeriesSet(chk ChunkSeriesSet) SeriesSet {
	return &chunkSetToSeriesSet{ChunkSeriesSet: chk}
}

func (c *chunkSetToSeriesSet) Next() bool {
	if c.Err() != nil || !c.ChunkSeriesSet.Next() {
		return false
	}

	iter := c.ChunkSeriesSet.At().Iterator()
	c.sameSeriesChunks = c.sameSeriesChunks[:0]

	for iter.Next() {
		c.sameSeriesChunks = append(
			c.sameSeriesChunks,
			newChunkToSeriesDecoder(c.ChunkSeriesSet.At().Labels(), iter.At()),
		)
	}

	if iter.Err() != nil {
		c.chkIterErr = iter.Err()
		return false
	}
	return true
}

func (c *chunkSetToSeriesSet) At() Series {
	// Series composed of same chunks for the same series.
	return ChainedSeriesMerge(c.sameSeriesChunks...)
}

func (c *chunkSetToSeriesSet) Err() error {
	if c.chkIterErr != nil {
		return c.chkIterErr
	}
	return c.ChunkSeriesSet.Err()
}

func newChunkToSeriesDecoder(labels labels.Labels, chk chunks.Meta) Series {
	return &SeriesEntry{
		Lset: labels,
		SampleIteratorFn: func() chunkenc.Iterator {
			// TODO(bwplotka): Can we provide any chunkenc buffer?
			return chk.Chunk.Iterator(nil)
		},
	}
}

type seriesSetToChunkSet struct {
	SeriesSet
}

// NewSeriesSetToChunkSet converts SeriesSet to ChunkSeriesSet by encoding chunks from samples.
func NewSeriesSetToChunkSet(chk SeriesSet) ChunkSeriesSet {
	return &seriesSetToChunkSet{SeriesSet: chk}
}

func (c *seriesSetToChunkSet) Next() bool {
	if c.Err() != nil || !c.SeriesSet.Next() {
		return false
	}
	return true
}

func (c *seriesSetToChunkSet) At() ChunkSeries {
	return &seriesToChunkEncoder{
		Series: c.SeriesSet.At(),
	}
}

func (c *seriesSetToChunkSet) Err() error {
	return c.SeriesSet.Err()
}

type seriesToChunkEncoder struct {
	Series
}

// TODO(bwplotka): Currently encoder will just naively build one chunk, without limit. Split it: https://github.com/prometheus/tsdb/issues/670
func (s *seriesToChunkEncoder) Iterator() chunks.Iterator {
	chk := chunkenc.NewXORChunk()
	app, err := chk.Appender()
	if err != nil {
		return errChunksIterator{err: err}
	}
	mint := int64(math.MaxInt64)
	maxt := int64(math.MinInt64)

	seriesIter := s.Series.Iterator()
	for seriesIter.Next() {
		t, v := seriesIter.At()
		app.Append(t, v)

		maxt = t
		if mint == math.MaxInt64 {
			mint = t
		}
	}
	if err := seriesIter.Err(); err != nil {
		return errChunksIterator{err: err}
	}

	return NewListChunkSeriesIterator(chunks.Meta{
		MinTime: mint,
		MaxTime: maxt,
		Chunk:   chk,
	})
}

type errChunksIterator struct {
	err error
}

func (e errChunksIterator) At() chunks.Meta { return chunks.Meta{} }
func (e errChunksIterator) Next() bool      { return false }
func (e errChunksIterator) Err() error      { return e.err }

// ExpandSamples iterates over all samples in the iterator, buffering all in slice.
// Optionally it takes samples constructor, useful when you want to compare sample slices with different
// sample implementations. if nil, sample type from this package will be used.
func ExpandSamples(iter chunkenc.Iterator, newSampleFn func(t int64, v float64) tsdbutil.Sample) ([]tsdbutil.Sample, error) {
	if newSampleFn == nil {
		newSampleFn = func(t int64, v float64) tsdbutil.Sample { return sample{t, v} }
	}

	var result []tsdbutil.Sample
	for iter.Next() {
		t, v := iter.At()
		// NaNs can't be compared normally, so substitute for another value.
		if math.IsNaN(v) {
			v = -42
		}
		result = append(result, newSampleFn(t, v))
	}
	return result, iter.Err()
}

// ExpandChunks iterates over all chunks in the iterator, buffering all in slice.
func ExpandChunks(iter chunks.Iterator) ([]chunks.Meta, error) {
	var result []chunks.Meta
	for iter.Next() {
		result = append(result, iter.At())
	}
	return result, iter.Err()
}
<|endoftext|>


<commit_before>// Copyright 2015, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 httputils_test

import (
	"errors"
	"net/http"
	"testing"

	"github.com/corestoreio/csfw/net/httputils"
	"github.com/stretchr/testify/assert"
)

func TestIsSecure(t *testing.T) {
	t.Log("TODO")
}

func TestIsBaseUrlCorrect(t *testing.T) {

	var nr = func(urlStr string) *http.Request {
		r, err := http.NewRequest("GET", urlStr, nil)
		if err != nil {
			t.Fatal(err)
		}
		return r
	}

	tests := []struct {
		req         *http.Request
		haveBaseURL string
		wantErr     error
	}{
		{nr("http://corestore.io/"), "http://corestore.io/", nil},
		{nr("http://www.corestore.io/"), "http://corestore.io/", httputils.ErrBaseUrlDoNotMatch},
		{nr("http://corestore.io/"), "https://corestore.io/", httputils.ErrBaseUrlDoNotMatch},
		{nr("http://corestore.io/"), "http://corestore.io/subpath", httputils.ErrBaseUrlDoNotMatch},
		{nr("http://corestore.io/subpath"), "http://corestore.io/subpath", nil},
		{nr("http://corestore.io/subpath"), "://cs.io", errors.New("parse ://cs.io: missing protocol scheme")},
	}
	for i, test := range tests {
		haveErr := httputils.IsBaseUrlCorrect(test.req, test.haveBaseURL)
		if test.wantErr != nil {
			assert.EqualError(t, haveErr, test.wantErr.Error(), "Index %d", i)
		} else {
			assert.NoError(t, haveErr, "Index %d", i)
		}
	}
}
<commit_msg>net/httputils: Add tests for IsSecure()<commit_after>// Copyright 2015, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 httputils_test

import (
	"errors"
	"net/http"
	"testing"

	"crypto/tls"

	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/net/httputils"
	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
)

func TestIsSecure(t *testing.T) {
	tests := []struct {
		ctx          context.Context
		req          *http.Request
		wantIsSecure bool
	}{
		{
			context.Background(),
			func() *http.Request {
				r, err := http.NewRequest("GET", "/", nil)
				if err != nil {
					t.Fatal(err)
				}
				r.TLS = new(tls.ConnectionState)
				return r
			}(),
			true,
		},
		{
			config.NewContextMockReader(context.Background(), config.WithMockValues(config.MockPV{
				config.MockPathScopeDefault(httputils.PathOffloaderHeader): "X_FORWARDED_PROTO",
			})),
			func() *http.Request {
				r, err := http.NewRequest("GET", "/", nil)
				if err != nil {
					t.Fatal(err)
				}
				r.Header.Set("HTTP_X_FORWARDED_PROTO", "https")
				return r
			}(),
			true,
		},
		{
			config.NewContextMockReader(context.Background(), config.WithMockValues(config.MockPV{
				config.MockPathScopeDefault(httputils.PathOffloaderHeader): "X_FORWARDED_PROTO",
			})),
			func() *http.Request {
				r, err := http.NewRequest("GET", "/", nil)
				if err != nil {
					t.Fatal(err)
				}
				r.Header.Set("HTTP_X_FORWARDED_PROTO", "tls")
				return r
			}(),
			false,
		},
		{
			config.NewContextMockReader(context.Background(), config.WithMockValues(config.MockPV{})),
			func() *http.Request {
				r, err := http.NewRequest("GET", "/", nil)
				if err != nil {
					t.Fatal(err)
				}
				r.Header.Set("HTTP_X_FORWARDED_PROTO", "does not matter")
				return r
			}(),
			false,
		},
	}
	for _, test := range tests {
		assert.Exactly(t, test.wantIsSecure, httputils.IsSecure(test.ctx, test.req))
	}
}

func TestIsBaseUrlCorrect(t *testing.T) {

	var nr = func(urlStr string) *http.Request {
		r, err := http.NewRequest("GET", urlStr, nil)
		if err != nil {
			t.Fatal(err)
		}
		return r
	}

	tests := []struct {
		req         *http.Request
		haveBaseURL string
		wantErr     error
	}{
		{nr("http://corestore.io/"), "http://corestore.io/", nil},
		{nr("http://www.corestore.io/"), "http://corestore.io/", httputils.ErrBaseUrlDoNotMatch},
		{nr("http://corestore.io/"), "https://corestore.io/", httputils.ErrBaseUrlDoNotMatch},
		{nr("http://corestore.io/"), "http://corestore.io/subpath", httputils.ErrBaseUrlDoNotMatch},
		{nr("http://corestore.io/subpath"), "http://corestore.io/subpath", nil},
		{nr("http://corestore.io/subpath"), "://cs.io", errors.New("parse ://cs.io: missing protocol scheme")},
	}
	for i, test := range tests {
		haveErr := httputils.IsBaseUrlCorrect(test.req, test.haveBaseURL)
		if test.wantErr != nil {
			assert.EqualError(t, haveErr, test.wantErr.Error(), "Index %d", i)
		} else {
			assert.NoError(t, haveErr, "Index %d", i)
		}
	}
}
<|endoftext|>


<commit_before>package command

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"path/filepath"
	"reflect"
	"sort"
	"testing"
)

func TestPush_noArgs(t *testing.T) {
	c := &PushCommand{Meta: testMeta(t)}
	code := c.Run(nil)
	if code != 1 {
		t.Fatalf("bad: %#v", code)
	}
}

func TestPush_multiArgs(t *testing.T) {
	c := &PushCommand{Meta: testMeta(t)}
	code := c.Run([]string{"one", "two"})
	if code != 1 {
		t.Fatalf("bad: %#v", code)
	}
}

func TestPush(t *testing.T) {
	var actual []string
	var actualOpts *uploadOpts
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actual = testArchive(t, r)
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push"), "template.json")}
	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expected := []string{
		archiveTemplateEntry,
		"template.json",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedBuilds := map[string]*uploadBuildInfo{
		"dummy": &uploadBuildInfo{
			Type: "dummy",
		},
	}
	if !reflect.DeepEqual(actualOpts.Builds, expectedBuilds) {
		t.Fatalf("bad: %#v", actualOpts.Builds)
	}
}

func TestPush_builds(t *testing.T) {
	var actualOpts *uploadOpts
	uploadFn := func(
		r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push-builds"), "template.json")}
	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expectedBuilds := map[string]*uploadBuildInfo{
		"dummy": &uploadBuildInfo{
			Type:     "dummy",
			Artifact: true,
		},
		"foo": &uploadBuildInfo{
			Type: "dummy",
		},
	}
	if !reflect.DeepEqual(actualOpts.Builds, expectedBuilds) {
		t.Fatalf("bad: %#v", actualOpts.Builds)
	}
}

func TestPush_noName(t *testing.T) {
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		return nil, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push-no-name"), "template.json")}
	if code := c.Run(args); code != 1 {
		fatalCommand(t, c.Meta)
	}
}

func TestPush_cliName(t *testing.T) {
	var actual []string
	var actualOpts *uploadOpts
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actual = testArchive(t, r)
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{
		"-name=foo/bar",
		filepath.Join(testFixture("push-no-name"), "template.json"),
	}

	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expected := []string{
		archiveTemplateEntry,
		"template.json",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestPush_uploadError(t *testing.T) {
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		return nil, nil, fmt.Errorf("bad")
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push"), "template.json")}
	if code := c.Run(args); code != 1 {
		fatalCommand(t, c.Meta)
	}
}

func TestPush_uploadErrorCh(t *testing.T) {
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		errCh := make(chan error, 1)
		errCh <- fmt.Errorf("bad")
		return nil, errCh, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push"), "template.json")}
	if code := c.Run(args); code != 1 {
		fatalCommand(t, c.Meta)
	}
}

func TestPush_vars(t *testing.T) {
	var actualOpts *uploadOpts
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{
		"-var", "name=foo/bar",
		filepath.Join(testFixture("push-vars"), "template.json"),
	}
	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expected := "foo/bar"
	if actualOpts.Slug != expected {
		t.Fatalf("bad: %#v", actualOpts.Slug)
	}
}

func testArchive(t *testing.T, r io.Reader) []string {
	// Finish the archiving process in-memory
	var buf bytes.Buffer
	if _, err := io.Copy(&buf, r); err != nil {
		t.Fatalf("err: %s", err)
	}

	gzipR, err := gzip.NewReader(&buf)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	tarR := tar.NewReader(gzipR)

	// Read all the entries
	result := make([]string, 0, 5)
	for {
		hdr, err := tarR.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		result = append(result, hdr.Name)
	}

	sort.Strings(result)
	return result
}
<commit_msg>Removed unused variable breaking gorename<commit_after>package command

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"path/filepath"
	"reflect"
	"sort"
	"testing"
)

func TestPush_noArgs(t *testing.T) {
	c := &PushCommand{Meta: testMeta(t)}
	code := c.Run(nil)
	if code != 1 {
		t.Fatalf("bad: %#v", code)
	}
}

func TestPush_multiArgs(t *testing.T) {
	c := &PushCommand{Meta: testMeta(t)}
	code := c.Run([]string{"one", "two"})
	if code != 1 {
		t.Fatalf("bad: %#v", code)
	}
}

func TestPush(t *testing.T) {
	var actual []string
	var actualOpts *uploadOpts
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actual = testArchive(t, r)
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push"), "template.json")}
	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expected := []string{
		archiveTemplateEntry,
		"template.json",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}

	expectedBuilds := map[string]*uploadBuildInfo{
		"dummy": &uploadBuildInfo{
			Type: "dummy",
		},
	}
	if !reflect.DeepEqual(actualOpts.Builds, expectedBuilds) {
		t.Fatalf("bad: %#v", actualOpts.Builds)
	}
}

func TestPush_builds(t *testing.T) {
	var actualOpts *uploadOpts
	uploadFn := func(
		r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push-builds"), "template.json")}
	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expectedBuilds := map[string]*uploadBuildInfo{
		"dummy": &uploadBuildInfo{
			Type:     "dummy",
			Artifact: true,
		},
		"foo": &uploadBuildInfo{
			Type: "dummy",
		},
	}
	if !reflect.DeepEqual(actualOpts.Builds, expectedBuilds) {
		t.Fatalf("bad: %#v", actualOpts.Builds)
	}
}

func TestPush_noName(t *testing.T) {
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		return nil, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push-no-name"), "template.json")}
	if code := c.Run(args); code != 1 {
		fatalCommand(t, c.Meta)
	}
}

func TestPush_cliName(t *testing.T) {
	var actual []string
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actual = testArchive(t, r)

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{
		"-name=foo/bar",
		filepath.Join(testFixture("push-no-name"), "template.json"),
	}

	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expected := []string{
		archiveTemplateEntry,
		"template.json",
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestPush_uploadError(t *testing.T) {
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		return nil, nil, fmt.Errorf("bad")
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push"), "template.json")}
	if code := c.Run(args); code != 1 {
		fatalCommand(t, c.Meta)
	}
}

func TestPush_uploadErrorCh(t *testing.T) {
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		errCh := make(chan error, 1)
		errCh <- fmt.Errorf("bad")
		return nil, errCh, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{filepath.Join(testFixture("push"), "template.json")}
	if code := c.Run(args); code != 1 {
		fatalCommand(t, c.Meta)
	}
}

func TestPush_vars(t *testing.T) {
	var actualOpts *uploadOpts
	uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
		actualOpts = opts

		doneCh := make(chan struct{})
		close(doneCh)
		return doneCh, nil, nil
	}

	c := &PushCommand{
		Meta:     testMeta(t),
		uploadFn: uploadFn,
	}

	args := []string{
		"-var", "name=foo/bar",
		filepath.Join(testFixture("push-vars"), "template.json"),
	}
	if code := c.Run(args); code != 0 {
		fatalCommand(t, c.Meta)
	}

	expected := "foo/bar"
	if actualOpts.Slug != expected {
		t.Fatalf("bad: %#v", actualOpts.Slug)
	}
}

func testArchive(t *testing.T, r io.Reader) []string {
	// Finish the archiving process in-memory
	var buf bytes.Buffer
	if _, err := io.Copy(&buf, r); err != nil {
		t.Fatalf("err: %s", err)
	}

	gzipR, err := gzip.NewReader(&buf)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	tarR := tar.NewReader(gzipR)

	// Read all the entries
	result := make([]string, 0, 5)
	for {
		hdr, err := tarR.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		result = append(result, hdr.Name)
	}

	sort.Strings(result)
	return result
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

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

var (
	Debugging    = false
	ErrorBuffer  = &bytes.Buffer{}
	ErrorWriter  = io.MultiWriter(os.Stderr, ErrorBuffer)
	OutputWriter = io.MultiWriter(os.Stdout, ErrorBuffer)
	RootCmd      = &cobra.Command{
		Use:   "git-lfs",
		Short: "Git LFS provides large file storage to Git.",
		Run: func(cmd *cobra.Command, args []string) {
			versionCommand(cmd, args)
			cmd.Usage()
		},
	}
)

// Error prints a formatted message to Stderr.  It also gets printed to the
// panic log if one is created for this command.
func Error(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(ErrorWriter, line)
}

// Print prints a formatted message to Stdout.  It also gets printed to the
// panic log if one is created for this command.
func Print(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(OutputWriter, line)
}

// Exit prints a formatted message and exits.
func Exit(format string, args ...interface{}) {
	Error(format, args...)
	os.Exit(2)
}

// Debug prints a formatted message if debugging is enabled.  The formatted
// message also shows up in the panic log, if created.
func Debug(format string, args ...interface{}) {
	if !Debugging {
		return
	}
	log.Printf(format, args...)
}

// LoggedError prints a formatted message to Stderr and writes a stack trace for
// the error to a log file without exiting.
func LoggedError(err error, format string, args ...interface{}) {
	Error(format, args...)
	file := handlePanic(err)

	if len(file) > 0 {
		fmt.Fprintf(os.Stderr, "\nErrors logged to %s\nUse `git lfs logs last` to view the log.\n", file)
	}
}

// Panic prints a formatted message, and writes a stack trace for the error to
// a log file before exiting.
func Panic(err error, format string, args ...interface{}) {
	LoggedError(err, format, args...)
	os.Exit(2)
}

func Run() {
	RootCmd.Execute()
}

func PipeMediaCommand(name string, args ...string) error {
	return PipeCommand("bin/"+name, args...)
}

func PipeCommand(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func requireStdin(msg string) {
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) != 0 {
		Error("Cannot read from STDIN. %s", msg)
		os.Exit(1)
	}
}

func handlePanic(err error) string {
	if err == nil {
		return ""
	}

	return logPanic(err, false)
}

func logEnv(w io.Writer) {
	for _, env := range lfs.Environ() {
		fmt.Fprintln(w, env)
	}
}

func logPanic(loggedError error, recursive bool) string {
	var fmtWriter io.Writer = os.Stderr

	if err := os.MkdirAll(lfs.LocalLogDir, 0755); err != nil {
		fmt.Fprintf(fmtWriter, "Unable to log panic to %s: %s\n\n", lfs.LocalLogDir, err.Error())
		return ""
	}

	now := time.Now()
	name := now.Format("20060102T150405.999999999")
	full := filepath.Join(lfs.LocalLogDir, name+".log")

	if file, err := os.Create(full); err != nil {
		if !recursive {
			defer func() {
				fmt.Fprintf(fmtWriter, "Unable to log panic to %s\n\n", full)
				logPanic(err, true)
			}()
		}
	} else {
		fmtWriter = file
		defer file.Close()
	}

	fmt.Fprintf(fmtWriter, "> %s", filepath.Base(os.Args[0]))
	if len(os.Args) > 0 {
		fmt.Fprintf(fmtWriter, " %s", strings.Join(os.Args[1:], " "))
	}
	fmt.Fprintln(fmtWriter)

	logEnv(fmtWriter)
	fmt.Fprintln(fmtWriter)

	fmtWriter.Write(ErrorBuffer.Bytes())
	fmt.Fprintln(fmtWriter)

	fmt.Fprintln(fmtWriter, loggedError.Error())

	if wErr, ok := loggedError.(ErrorWithStack); ok {
		fmt.Fprintln(fmtWriter, wErr.InnerError())
		for key, value := range wErr.Context() {
			fmt.Fprintf(fmtWriter, "%s=%s\n", key, value)
		}
		fmtWriter.Write(wErr.Stack())
	} else {
		fmtWriter.Write(lfs.Stack())
	}

	return full
}

type ErrorWithStack interface {
	Context() map[string]string
	InnerError() string
	Stack() []byte
}

func init() {
	log.SetOutput(ErrorWriter)
}
<commit_msg>アー アアアア アーアー<commit_after>package commands

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

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

var (
	Debugging    = false
	ErrorBuffer  = &bytes.Buffer{}
	ErrorWriter  = io.MultiWriter(os.Stderr, ErrorBuffer)
	OutputWriter = io.MultiWriter(os.Stdout, ErrorBuffer)
	RootCmd      = &cobra.Command{
		Use:   "git-lfs",
		Short: "Git LFS provides large file storage to Git.",
		Run: func(cmd *cobra.Command, args []string) {
			versionCommand(cmd, args)
			cmd.Usage()
		},
	}
)

// Error prints a formatted message to Stderr.  It also gets printed to the
// panic log if one is created for this command.
func Error(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(ErrorWriter, line)
}

// Print prints a formatted message to Stdout.  It also gets printed to the
// panic log if one is created for this command.
func Print(format string, args ...interface{}) {
	line := fmt.Sprintf(format, args...)
	fmt.Fprintln(OutputWriter, line)
}

// Exit prints a formatted message and exits.
func Exit(format string, args ...interface{}) {
	Error(format, args...)
	os.Exit(2)
}

// Debug prints a formatted message if debugging is enabled.  The formatted
// message also shows up in the panic log, if created.
func Debug(format string, args ...interface{}) {
	if !Debugging {
		return
	}
	log.Printf(format, args...)
}

// LoggedError prints a formatted message to Stderr and writes a stack trace for
// the error to a log file without exiting.
func LoggedError(err error, format string, args ...interface{}) {
	Error(format, args...)
	file := handlePanic(err)

	if len(file) > 0 {
		fmt.Fprintf(os.Stderr, "\nErrors logged to %s\nUse `git lfs logs last` to view the log.\n", file)
	}
}

// Panic prints a formatted message, and writes a stack trace for the error to
// a log file before exiting.
func Panic(err error, format string, args ...interface{}) {
	LoggedError(err, format, args...)
	os.Exit(2)
}

func Run() {
	RootCmd.Execute()
}

func PipeMediaCommand(name string, args ...string) error {
	return PipeCommand("bin/"+name, args...)
}

func PipeCommand(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func requireStdin(msg string) {
	stat, _ := os.Stdin.Stat()
	if (stat.Mode() & os.ModeCharDevice) != 0 {
		Error("Cannot read from STDIN. %s", msg)
		os.Exit(1)
	}
}

func handlePanic(err error) string {
	if err == nil {
		return ""
	}

	return logPanic(err, false)
}

func logEnv(w io.Writer) {
	for _, env := range lfs.Environ() {
		fmt.Fprintln(w, env)
	}
}

func logPanic(loggedError error, recursive bool) string {
	var fmtWriter io.Writer = os.Stderr

	if err := os.MkdirAll(lfs.LocalLogDir, 0755); err != nil {
		fmt.Fprintf(fmtWriter, "Unable to log panic to %s: %s\n\n", lfs.LocalLogDir, err.Error())
		return ""
	}

	now := time.Now()
	name := now.Format("20060102T150405.999999999")
	full := filepath.Join(lfs.LocalLogDir, name+".log")

	if file, err := os.Create(full); err != nil {
		if !recursive {
			filename := full
			full = ""
			defer func() {
				fmt.Fprintf(fmtWriter, "Unable to log panic to %s\n\n", filename)
				logPanic(err, true)
			}()
		}
	} else {
		fmtWriter = file
		defer file.Close()
	}

	fmt.Fprintf(fmtWriter, "> %s", filepath.Base(os.Args[0]))
	if len(os.Args) > 0 {
		fmt.Fprintf(fmtWriter, " %s", strings.Join(os.Args[1:], " "))
	}
	fmt.Fprintln(fmtWriter)

	logEnv(fmtWriter)
	fmt.Fprintln(fmtWriter)

	fmtWriter.Write(ErrorBuffer.Bytes())
	fmt.Fprintln(fmtWriter)

	fmt.Fprintln(fmtWriter, loggedError.Error())

	if wErr, ok := loggedError.(ErrorWithStack); ok {
		fmt.Fprintln(fmtWriter, wErr.InnerError())
		for key, value := range wErr.Context() {
			fmt.Fprintf(fmtWriter, "%s=%s\n", key, value)
		}
		fmtWriter.Write(wErr.Stack())
	} else {
		fmtWriter.Write(lfs.Stack())
	}

	return full
}

type ErrorWithStack interface {
	Context() map[string]string
	InnerError() string
	Stack() []byte
}

func init() {
	log.SetOutput(ErrorWriter)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Doctl Authors All rights reserved.
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 commands

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/digitalocean/doctl"
	"github.com/digitalocean/doctl/commands/displayers"
	"github.com/digitalocean/doctl/do"
	"github.com/digitalocean/godo"
	"github.com/spf13/cobra"
	k8sapiv1 "k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
)

type dockerConfig struct {
	Auths map[string]struct {
		Auth string `json:"auth,omitempty"`
	} `json:"auths"`
}

// Registry creates the registry command
func Registry() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "registry",
			Aliases: []string{"reg", "r"},
			Short:   "[Beta] Display commands for working with container registries",
			Long:    "[Beta] The subcommands of `doctl registry` create, manage, and allow access to your private container registry.",
			Hidden:  true,
		},
	}

	cmd.AddCommand(Repository())

	createRegDesc := "This command creates a new private container registry with the provided name."
	CmdBuilder(cmd, RunRegistryCreate, "create <registry-name>",
		"Create a private container registry", createRegDesc, Writer)

	getRegDesc := "This command retrieves details about a private container registry including its name and the endpoint used to access it."
	CmdBuilder(cmd, RunRegistryGet, "get", "Retrieve details about a container registry",
		getRegDesc, Writer, aliasOpt("g"), displayerType(&displayers.Registry{}))

	deleteRegDesc := "This command permanently deletes a private container registry and all of its contents."
	cmdRunRegistryDelete := CmdBuilder(cmd, RunRegistryDelete, "delete",
		"Delete a container registry", deleteRegDesc, Writer, aliasOpt("d", "del", "rm"))
	AddBoolFlag(cmdRunRegistryDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force registry delete")

	loginRegDesc := "This command logs in Docker so that pull and push commands to your private container registry will be authenticated."
	CmdBuilder(cmd, RunRegistryLogin, "login", "Log in Docker to a container registry",
		loginRegDesc, Writer)

	logoutRegDesc := "This command logs Docker out of the private container registry, revoking access to it."
	CmdBuilder(cmd, RunRegistryLogout, "logout", "Log out Docker from a container registry",
		logoutRegDesc, Writer)

	kubeManifestDesc := `This command outputs a YAML-formated Kubernetes secret manifest that can be used to grant a Kubernetes cluster pull access to your private container registry.

Redirect the command's output to a file to save the manifest for later use or pipe it directly to kubectl to create the secret in your cluster:

    doctl registry kubernetes-manifest | kubectl apply -f -
`
	cmdRunKubernetesManifest := CmdBuilder(cmd, RunKubernetesManifest, "kubernetes-manifest",
		"Generate a Kubernetes secret manifest for a registry",
		kubeManifestDesc, Writer, aliasOpt("k8s"))
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectName, "", "",
		"The secret name to create. Defaults to the registry name prefixed with \"registry-\"")
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectNamespace, "",
		"default", "The Kubernetes namespace to hold the secret")

	return cmd
}

// Repository creates the repository sub-command
func Repository() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "repository",
			Aliases: []string{"repo", "r"},
			Short:   "[Beta] Display commands for working with repositories in a container registry",
			Long:    "[Beta] The subcommands of `doctl registry repository` help you command actions related to a repository.",
			Hidden:  true,
		},
	}

	listRepositoriesDesc := `
	This command retrieves information about repositories in a registry, including:
	- The repository name
	- The latest tag of the repository
	- The compressed size for the latest tag
	- The manifest digest for the latest tag
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositories, "list",
		"List repositories for a container registry", listRepositoriesDesc,
		Writer, aliasOpt("ls"), displayerType(&displayers.Repository{}),
	)

	listRepositoryTagsDesc := `
	This command retrieves information about tags in a repository, including:
	- The tag name
	- The compressed size
	- The manifest digest
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositoryTags, "list-tags <repository>",
		"List tags for a repository in a container registry", listRepositoryTagsDesc,
		Writer, aliasOpt("lt"), displayerType(&displayers.RepositoryTag{}),
	)

	deleteTagDesc := "This command permanently deletes one or more repository tags."
	cmdRunRepositoryDeleteTag := CmdBuilder(
		cmd,
		RunRepositoryDeleteTag,
		"delete-tag <repository> <tag>...",
		"Delete one or more container repository tags",
		deleteTagDesc,
		Writer,
		aliasOpt("dt"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteTag, doctl.ArgForce, doctl.ArgShortForce, false, "Force tag deletion")

	deleteManifestDesc := "This command permanently deletes one or more repository manifests by digest."
	cmdRunRepositoryDeleteManifest := CmdBuilder(
		cmd,
		RunRepositoryDeleteManifest,
		"delete-manifest <repository> <manifest-digest>...",
		"Delete one or more container repository manifests by digest",
		deleteManifestDesc,
		Writer,
		aliasOpt("dm"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteManifest, doctl.ArgForce, doctl.ArgShortForce, false, "Force manifest deletion")

	return cmd
}

// Registry Run Commands

// RunRegistryCreate creates a registry
func RunRegistryCreate(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	name := c.Args[0]
	rs := c.Registry()

	rcr := &godo.RegistryCreateRequest{Name: name}
	r, err := rs.Create(rcr)
	if err != nil {
		return err
	}

	return displayRegistries(c, *r)
}

// RunRegistryGet returns the registry
func RunRegistryGet(c *CmdConfig) error {
	reg, err := c.Registry().Get()
	if err != nil {
		return err
	}

	return displayRegistries(c, *reg)
}

// RunRegistryDelete delete the registry
func RunRegistryDelete(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if !force && AskForConfirm("delete registry") != nil {
		return fmt.Errorf("operation aborted")
	}

	return c.Registry().Delete()
}

// store execCommand in a variable. Lets us override it while testing
var execCommand = exec.Command

// RunRegistryLogin logs in Docker to the registry
func RunRegistryLogin(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	fmt.Printf("Logging Docker in to %s\n", c.Registry().Endpoint())

	creds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: true,
	})
	if err != nil {
		return err
	}

	var dc dockerConfig
	err = json.Unmarshal(creds.DockerConfigJSON, &dc)
	if err != nil {
		return err
	}

	// read the login credentials from the docker config
	for host, conf := range dc.Auths {
		// decode and split into username + password
		creds, err := base64.StdEncoding.DecodeString(conf.Auth)
		if err != nil {
			return err
		}

		splitCreds := strings.Split(string(creds), ":")
		if len(splitCreds) != 2 {
			return fmt.Errorf("got invalid docker credentials")
		}
		user, pass := splitCreds[0], splitCreds[1]

		// log in via the docker cli
		args := []string{
			"login", host,
			"-u", user,
			"--password-stdin",
		}
		cmd := execCommand("docker", args...)
		cmd.Stdin = strings.NewReader(pass)
		cmd.Stdout = c.Out
		cmd.Stderr = c.Out

		err = cmd.Run()
		if err != nil {
			return err
		}
	}

	return nil
}

// RunKubernetesManifest prints a Kubernetes manifest that provides read/pull access to the registry
func RunKubernetesManifest(c *CmdConfig) error {
	secretName, err := c.Doit.GetString(c.NS, doctl.ArgObjectName)
	if err != nil {
		return err
	}
	secretNamespace, err := c.Doit.GetString(c.NS, doctl.ArgObjectNamespace)
	if err != nil {
		return err
	}

	// if no secret name supplied, use the registry name
	if secretName == "" {
		reg, err := c.Registry().Get()
		if err != nil {
			return err
		}
		secretName = "registry-" + reg.Name
	}

	// fetch docker config
	dockerCreds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: false,
	})
	if err != nil {
		return err
	}

	// create the manifest for the secret
	secret := &k8sapiv1.Secret{
		TypeMeta: k8smetav1.TypeMeta{
			Kind:       "Secret",
			APIVersion: "v1",
		},
		ObjectMeta: k8smetav1.ObjectMeta{
			Name:      secretName,
			Namespace: secretNamespace,
		},
		Type: k8sapiv1.SecretTypeDockerConfigJson,
		Data: map[string][]byte{
			".dockerconfigjson": dockerCreds.DockerConfigJSON,
		},
	}

	serializer := k8sjson.NewSerializerWithOptions(
		k8sjson.DefaultMetaFactory, nil, nil,
		k8sjson.SerializerOptions{
			Yaml:   true,
			Pretty: true,
			Strict: true,
		},
	)

	return serializer.Encode(secret, c.Out)
}

// RunRegistryLogout logs Docker out of the registry
func RunRegistryLogout(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	cmd := execCommand("docker", "logout", c.Registry().Endpoint())
	cmd.Stdout = c.Out
	cmd.Stderr = c.Out

	return cmd.Run()
}

// Repository Run Commands

// RunListRepositories lists repositories for the registry
func RunListRepositories(c *CmdConfig) error {
	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repositories, err := c.Registry().ListRepositories(registry.Name)
	if err != nil {
		return err
	}

	return displayRepositories(c, repositories...)
}

// RunListRepositoryTags lists tags for the repository in a registry
func RunListRepositoryTags(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	tags, err := c.Registry().ListRepositoryTags(registry.Name, c.Args[0])
	if err != nil {
		return err
	}

	return displayRepositoryTags(c, tags...)
}

// RunRepositoryDeleteTag deletes one or more repository tags
func RunRepositoryDeleteTag(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	tags := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository tag(s)", len(tags))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, tag := range tags {
		if err := c.Registry().DeleteTag(registry.Name, repository, tag); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository tags: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

// RunRepositoryDeleteManifest deletes one or more repository manifests by digest
func RunRepositoryDeleteManifest(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	digests := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository manifest(s) by digest (including associated tags)", len(digests))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, digest := range digests {
		if err := c.Registry().DeleteManifest(registry.Name, repository, digest); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository manifests: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

func displayRegistries(c *CmdConfig, registries ...do.Registry) error {
	item := &displayers.Registry{
		Registries: registries,
	}
	return c.Display(item)
}

func displayRepositories(c *CmdConfig, repositories ...do.Repository) error {
	item := &displayers.Repository{
		Repositories: repositories,
	}
	return c.Display(item)
}

func displayRepositoryTags(c *CmdConfig, tags ...do.RepositoryTag) error {
	item := &displayers.RepositoryTag{
		Tags: tags,
	}
	return c.Display(item)
}
<commit_msg>Update Container Registry to EA status<commit_after>/*
Copyright 2018 The Doctl Authors All rights reserved.
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 commands

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/digitalocean/doctl"
	"github.com/digitalocean/doctl/commands/displayers"
	"github.com/digitalocean/doctl/do"
	"github.com/digitalocean/godo"
	"github.com/spf13/cobra"
	k8sapiv1 "k8s.io/api/core/v1"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
)

type dockerConfig struct {
	Auths map[string]struct {
		Auth string `json:"auth,omitempty"`
	} `json:"auths"`
}

// Registry creates the registry command
func Registry() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "registry",
			Aliases: []string{"reg", "r"},
			Short:   "[EA] Display commands for working with container registries",
			Long:    "[EA] The subcommands of `doctl registry` create, manage, and allow access to your private container registry.",
		},
	}

	cmd.AddCommand(Repository())

	createRegDesc := "This command creates a new private container registry with the provided name."
	CmdBuilder(cmd, RunRegistryCreate, "create <registry-name>",
		"Create a private container registry", createRegDesc, Writer)

	getRegDesc := "This command retrieves details about a private container registry including its name and the endpoint used to access it."
	CmdBuilder(cmd, RunRegistryGet, "get", "Retrieve details about a container registry",
		getRegDesc, Writer, aliasOpt("g"), displayerType(&displayers.Registry{}))

	deleteRegDesc := "This command permanently deletes a private container registry and all of its contents."
	cmdRunRegistryDelete := CmdBuilder(cmd, RunRegistryDelete, "delete",
		"Delete a container registry", deleteRegDesc, Writer, aliasOpt("d", "del", "rm"))
	AddBoolFlag(cmdRunRegistryDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force registry delete")

	loginRegDesc := "This command logs in Docker so that pull and push commands to your private container registry will be authenticated."
	CmdBuilder(cmd, RunRegistryLogin, "login", "Log in Docker to a container registry",
		loginRegDesc, Writer)

	logoutRegDesc := "This command logs Docker out of the private container registry, revoking access to it."
	CmdBuilder(cmd, RunRegistryLogout, "logout", "Log out Docker from a container registry",
		logoutRegDesc, Writer)

	kubeManifestDesc := `This command outputs a YAML-formated Kubernetes secret manifest that can be used to grant a Kubernetes cluster pull access to your private container registry.

Redirect the command's output to a file to save the manifest for later use or pipe it directly to kubectl to create the secret in your cluster:

    doctl registry kubernetes-manifest | kubectl apply -f -
`
	cmdRunKubernetesManifest := CmdBuilder(cmd, RunKubernetesManifest, "kubernetes-manifest",
		"Generate a Kubernetes secret manifest for a registry",
		kubeManifestDesc, Writer, aliasOpt("k8s"))
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectName, "", "",
		"The secret name to create. Defaults to the registry name prefixed with \"registry-\"")
	AddStringFlag(cmdRunKubernetesManifest, doctl.ArgObjectNamespace, "",
		"default", "The Kubernetes namespace to hold the secret")

	return cmd
}

// Repository creates the repository sub-command
func Repository() *Command {
	cmd := &Command{
		Command: &cobra.Command{
			Use:     "repository",
			Aliases: []string{"repo", "r"},
			Short:   "[EA] Display commands for working with repositories in a container registry",
			Long:    "[EA] The subcommands of `doctl registry repository` help you command actions related to a repository.",
		},
	}

	listRepositoriesDesc := `
	This command retrieves information about repositories in a registry, including:
	- The repository name
	- The latest tag of the repository
	- The compressed size for the latest tag
	- The manifest digest for the latest tag
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositories, "list",
		"List repositories for a container registry", listRepositoriesDesc,
		Writer, aliasOpt("ls"), displayerType(&displayers.Repository{}),
	)

	listRepositoryTagsDesc := `
	This command retrieves information about tags in a repository, including:
	- The tag name
	- The compressed size
	- The manifest digest
	- The last updated timestamp
	`
	CmdBuilder(
		cmd,
		RunListRepositoryTags, "list-tags <repository>",
		"List tags for a repository in a container registry", listRepositoryTagsDesc,
		Writer, aliasOpt("lt"), displayerType(&displayers.RepositoryTag{}),
	)

	deleteTagDesc := "This command permanently deletes one or more repository tags."
	cmdRunRepositoryDeleteTag := CmdBuilder(
		cmd,
		RunRepositoryDeleteTag,
		"delete-tag <repository> <tag>...",
		"Delete one or more container repository tags",
		deleteTagDesc,
		Writer,
		aliasOpt("dt"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteTag, doctl.ArgForce, doctl.ArgShortForce, false, "Force tag deletion")

	deleteManifestDesc := "This command permanently deletes one or more repository manifests by digest."
	cmdRunRepositoryDeleteManifest := CmdBuilder(
		cmd,
		RunRepositoryDeleteManifest,
		"delete-manifest <repository> <manifest-digest>...",
		"Delete one or more container repository manifests by digest",
		deleteManifestDesc,
		Writer,
		aliasOpt("dm"),
	)
	AddBoolFlag(cmdRunRepositoryDeleteManifest, doctl.ArgForce, doctl.ArgShortForce, false, "Force manifest deletion")

	return cmd
}

// Registry Run Commands

// RunRegistryCreate creates a registry
func RunRegistryCreate(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	name := c.Args[0]
	rs := c.Registry()

	rcr := &godo.RegistryCreateRequest{Name: name}
	r, err := rs.Create(rcr)
	if err != nil {
		return err
	}

	return displayRegistries(c, *r)
}

// RunRegistryGet returns the registry
func RunRegistryGet(c *CmdConfig) error {
	reg, err := c.Registry().Get()
	if err != nil {
		return err
	}

	return displayRegistries(c, *reg)
}

// RunRegistryDelete delete the registry
func RunRegistryDelete(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if !force && AskForConfirm("delete registry") != nil {
		return fmt.Errorf("operation aborted")
	}

	return c.Registry().Delete()
}

// store execCommand in a variable. Lets us override it while testing
var execCommand = exec.Command

// RunRegistryLogin logs in Docker to the registry
func RunRegistryLogin(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	fmt.Printf("Logging Docker in to %s\n", c.Registry().Endpoint())

	creds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: true,
	})
	if err != nil {
		return err
	}

	var dc dockerConfig
	err = json.Unmarshal(creds.DockerConfigJSON, &dc)
	if err != nil {
		return err
	}

	// read the login credentials from the docker config
	for host, conf := range dc.Auths {
		// decode and split into username + password
		creds, err := base64.StdEncoding.DecodeString(conf.Auth)
		if err != nil {
			return err
		}

		splitCreds := strings.Split(string(creds), ":")
		if len(splitCreds) != 2 {
			return fmt.Errorf("got invalid docker credentials")
		}
		user, pass := splitCreds[0], splitCreds[1]

		// log in via the docker cli
		args := []string{
			"login", host,
			"-u", user,
			"--password-stdin",
		}
		cmd := execCommand("docker", args...)
		cmd.Stdin = strings.NewReader(pass)
		cmd.Stdout = c.Out
		cmd.Stderr = c.Out

		err = cmd.Run()
		if err != nil {
			return err
		}
	}

	return nil
}

// RunKubernetesManifest prints a Kubernetes manifest that provides read/pull access to the registry
func RunKubernetesManifest(c *CmdConfig) error {
	secretName, err := c.Doit.GetString(c.NS, doctl.ArgObjectName)
	if err != nil {
		return err
	}
	secretNamespace, err := c.Doit.GetString(c.NS, doctl.ArgObjectNamespace)
	if err != nil {
		return err
	}

	// if no secret name supplied, use the registry name
	if secretName == "" {
		reg, err := c.Registry().Get()
		if err != nil {
			return err
		}
		secretName = "registry-" + reg.Name
	}

	// fetch docker config
	dockerCreds, err := c.Registry().DockerCredentials(&godo.RegistryDockerCredentialsRequest{
		ReadWrite: false,
	})
	if err != nil {
		return err
	}

	// create the manifest for the secret
	secret := &k8sapiv1.Secret{
		TypeMeta: k8smetav1.TypeMeta{
			Kind:       "Secret",
			APIVersion: "v1",
		},
		ObjectMeta: k8smetav1.ObjectMeta{
			Name:      secretName,
			Namespace: secretNamespace,
		},
		Type: k8sapiv1.SecretTypeDockerConfigJson,
		Data: map[string][]byte{
			".dockerconfigjson": dockerCreds.DockerConfigJSON,
		},
	}

	serializer := k8sjson.NewSerializerWithOptions(
		k8sjson.DefaultMetaFactory, nil, nil,
		k8sjson.SerializerOptions{
			Yaml:   true,
			Pretty: true,
			Strict: true,
		},
	)

	return serializer.Encode(secret, c.Out)
}

// RunRegistryLogout logs Docker out of the registry
func RunRegistryLogout(c *CmdConfig) error {
	// check if docker is installed
	if _, err := exec.LookPath("docker"); err != nil {
		return fmt.Errorf("unable to find the Docker CLI binary. Make sure docker is installed")
	}

	cmd := execCommand("docker", "logout", c.Registry().Endpoint())
	cmd.Stdout = c.Out
	cmd.Stderr = c.Out

	return cmd.Run()
}

// Repository Run Commands

// RunListRepositories lists repositories for the registry
func RunListRepositories(c *CmdConfig) error {
	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repositories, err := c.Registry().ListRepositories(registry.Name)
	if err != nil {
		return err
	}

	return displayRepositories(c, repositories...)
}

// RunListRepositoryTags lists tags for the repository in a registry
func RunListRepositoryTags(c *CmdConfig) error {
	if len(c.Args) != 1 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	tags, err := c.Registry().ListRepositoryTags(registry.Name, c.Args[0])
	if err != nil {
		return err
	}

	return displayRepositoryTags(c, tags...)
}

// RunRepositoryDeleteTag deletes one or more repository tags
func RunRepositoryDeleteTag(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	tags := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository tag(s)", len(tags))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, tag := range tags {
		if err := c.Registry().DeleteTag(registry.Name, repository, tag); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository tags: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

// RunRepositoryDeleteManifest deletes one or more repository manifests by digest
func RunRepositoryDeleteManifest(c *CmdConfig) error {
	force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
	if err != nil {
		return err
	}

	if len(c.Args) < 2 {
		return doctl.NewMissingArgsErr(c.NS)
	}

	registry, err := c.Registry().Get()
	if err != nil {
		return fmt.Errorf("failed to get registry: %w", err)
	}

	repository := c.Args[0]
	digests := c.Args[1:]

	if !force && AskForConfirm(fmt.Sprintf("delete %d repository manifest(s) by digest (including associated tags)", len(digests))) != nil {
		return fmt.Errorf("operation aborted")
	}

	var errors []string
	for _, digest := range digests {
		if err := c.Registry().DeleteManifest(registry.Name, repository, digest); err != nil {
			errors = append(errors, err.Error())
		}
	}

	if len(errors) > 0 {
		return fmt.Errorf("failed to delete all repository manifests: \n%s", strings.Join(errors, "\n"))
	}

	return nil
}

func displayRegistries(c *CmdConfig, registries ...do.Registry) error {
	item := &displayers.Registry{
		Registries: registries,
	}
	return c.Display(item)
}

func displayRepositories(c *CmdConfig, repositories ...do.Repository) error {
	item := &displayers.Repository{
		Repositories: repositories,
	}
	return c.Display(item)
}

func displayRepositoryTags(c *CmdConfig, tags ...do.RepositoryTag) error {
	item := &displayers.RepositoryTag{
		Tags: tags,
	}
	return c.Display(item)
}
<|endoftext|>