<commit_before>package hayes

import (
	"time"
	"strings"
	"fmt"
)

// ATA
func (m *Modem) answer() error {
	if !m.getLineBusy()  {
		m.log.Print("Can't answer, line isn't ringing (not busy)")
		return ERROR
	}
	if m.offHook() {
		m.log.Print("Can't answer, line off hook already")
		return ERROR
	}

	m.goOffHook()
	time.Sleep(400 * time.Millisecond) // Simulate Carrier Detect delay
	m.raiseCD()
	m.mode = DATAMODE
	m.connect_speed = 38400	// We only go fast...
	return CONNECT
}

// ATZ
// Setup/reset modem.  Leaves RTS & CTS down.
func (m *Modem) reset() error {
	var err error = OK

	m.log.Print("Resetting modem")

	m.goOnHook()
	m.setLineBusy(false)
	m.lowerDSR()
	m.lowerCTS()
	m.lowerRI()
	m.stopTimer()

	m.echoInCmdMode = true  // Echo local keypresses
	m.quiet = false		// Modem offers return status
	m.verbose = true	// Text return codes
	m.volume = 1		// moderate volume
	m.speakermode = 1	// on until other modem heard
	m.lastcmd = ""
	m.lastdialed = ""
	m.connect_speed = 0
	m.connectMsgSpeed = true
	m.busyDetect = true
	m.extendedResultCodes = true
	m.resetRegs()
	m.resetTimer()
	m.addressbook, err = LoadAddressBook()
	if err != nil {
		m.log.Print(err)
	}

	return err
}

// AT&...
// Only support &V for now
func (m *Modem) ampersand(cmd string) error {
	var s string

	if cmd != "&V" {
		return ERROR
	}

	b := func(p bool) (string) {
		if p {
			return"1 "
		}
		return "0"
	};
	i := func(p int) (string) {
		return fmt.Sprintf("%d", p)
	};
	x := func(r, b bool) (string) {
		if (r == false && b == false) {
			return "0"
		}
		if (r == true && b == false) {
			return "1"
		}
		if (r == true && b == true) {
			return "7"
		}
		return "0"
	};

	s += "E" + b(m.echoInCmdMode)
	s += "F1"		// For Hayes 1200 compatability
	s += "L" + i(m.volume)
	s += "M" + i(m.speakermode)
	s += "Q" + b(m.quiet)
	s += "V" + b(m.verbose)
	s += "W" + b(m.connectMsgSpeed)
	s += "X" + x(m.extendedResultCodes, m.busyDetect)
	s += "\n"
	s += m.registers.String()
	m.serial.Println(s)
	return nil
}

// process each command
func (m *Modem) processCommands(commands []string) error {
	var status error
	var cmd string

	m.log.Printf("entering PC: %+v\n", commands)
	status = OK
	for _, cmd = range commands {
		m.log.Printf("Processing: %s", cmd)
		switch cmd[0] {
		case 'A':
			status = m.answer()
		case 'Z':
			status = m.reset()
			time.Sleep(250 * time.Millisecond)
			m.raiseDSR()
			m.raiseCTS()
		case 'E':
			if cmd[1] == '0' {
				m.echoInCmdMode = false
			} else {
				m.echoInCmdMode = true
			}
		case 'F':	// Online Echo mode, F1 assumed for backwards
			        // compatability after Hayes 1200
			status = OK
		case 'H':
			if cmd[1] == '0' {
				status = m.goOnHook()
			} else if cmd[1] == '1' {
				status = m.goOffHook()
			} else {
				status = ERROR
			}
		case 'Q':
			if cmd[1] == '0' {
				m.quiet = true
			} else {
				m.quiet = false
			}
		case 'V':
			if cmd[1] == '0' {
				m.verbose = true
			} else {
				m.verbose = false
			}
		case 'L':
			switch cmd[1] {
			case '0': m.volume = 0
			case '1': m.volume = 1
			case '2': m.volume = 2
			case '3': m.volume = 3
			}
		case 'M':
			switch cmd[1] {
			case '0': m.speakermode = 0
			case '1': m.speakermode = 1
			case '2': m.speakermode = 2
			}
		case 'O':
			m.mode = DATAMODE
			status = OK
		case 'W':
			switch cmd[1] {
			case '0': m.connectMsgSpeed = false
			case '1', '2': m.connectMsgSpeed = true
			}
		case 'X':	// Change result codes displayed
			switch cmd[1] {
			case '0':
				m.extendedResultCodes = false
				m.busyDetect = false
			case '1', '2':
				m.extendedResultCodes = true
				m.busyDetect = false
			case '3', '4', '5', '6', '7':
				m.extendedResultCodes = true
				m.busyDetect = true
			}
		case 'D':
			status = m.dial(cmd)
		case 'S':
			status = m.registerCmd(cmd)
		case '&':
			status = m.ampersand(cmd)
		case '*':
			status = m.debug(cmd)
		default:
			status = ERROR
		}
		if status != OK {
			break
		}
	}
	return status
}

// Helper function to parse non-complex AT commands (everthing except ATS.., ATD...)
func parse(cmd string, opts string) (string, int, error) {

	cmd = strings.ToUpper(cmd)
	if len(cmd) == 1 {
		return cmd + "0", 1, nil
	}

	if strings.ContainsAny(cmd[1:2], opts) {
		return cmd[:2],  2, nil
	}

	return "", 0, fmt.Errorf("Bad command: %s", cmd)
}

// +++
func (m *Modem) command(cmdstring string) {
	var commands []string
	var s, opts string
	var i int
	var status error
	var err error

	// Process here is to parse the entire command string into
	// discrete commands, then execute those discrete commands in
	// the order they were given to us.  This makes syntax
	// checking/failures happen before any commands are executed
	// which is, if I recall correctly, how this works in the real
	// hardware.  Note that the command codes ("DT", "X", etc.)
	// all must be upper case for the rest of the parsing system
	// to work, but the entire command string should be left as it
	// was handed to us.  This is so that we can embed passwords
	// in the extended dial command (ATDE, specifically).

	m.log.Print("command: ", cmdstring)

	if len(cmdstring) < 2  {
		m.log.Print("Cmd too short")
		m.prstatus(ERROR)
		return
	}

	if strings.ToUpper(cmdstring) == "AT" {
		m.prstatus(OK)
		return
	}

	if strings.ToUpper(cmdstring[:2]) != "AT" {
		m.log.Print("Malformed command")
		m.prstatus(ERROR)
		return
	}

	cmd := cmdstring[2:] 		// Skip the 'AT'
	c := 0

	commands = nil
	status = OK
	savecmds := true
	for  c < len(cmd) && status == OK {
		switch (cmd[c]) {
		case 'D', 'd':
			s, i, err = parseDial(cmd[c:])
			if err != nil {
				m.prstatus(ERROR)
				return
			}
			commands = append(commands, s)
			c += i
			continue
		case 'S', 's':
			s, i, err = parseRegisters(cmd[c:])
			if err != nil {
				m.prstatus(ERROR)
				return
			}
			commands = append(commands, s)
			c += i
			continue
		case '*': 	// Custom debug registers
			s, i, err = parseDebug(cmd[c:])
			if err != nil {
				m.prstatus(ERROR)
				return
			}
			commands = append(commands, s)
			c += i
			continue
		case 'A', 'a':
			opts = "0"
		case 'E', 'e', 'H', 'h', 'Q', 'q', 'V', 'v', 'Z', 'z':
			opts = "01"
		case 'L', 'l':
			opts = "0123"
		case 'M', 'm', 'W', 'w':
			opts = "012"
		case 'O', 'o':
			opts = "O"
		case 'X', 'x':
			opts = "01234567"
		case '&':
			opts = "V"
		default:
			m.log.Printf("Unknown command: %s", cmd)
			m.prstatus(ERROR)
			return
		}
		s, i, err = parse(cmd[c:], opts)
		if err != nil {
			m.prstatus(ERROR)
			return
		}
		commands = append(commands, s)
		c += i
	}

	m.log.Print("Command array: %+v", commands)
	status = m.processCommands(commands)
	m.prstatus(status)

	if savecmds && status == OK {
		m.lastcmd = cmdstring
	}
}
<commit_msg>Fix AT&V formatting<commit_after>package hayes

import (
	"time"
	"strings"
	"fmt"
)

// ATA
func (m *Modem) answer() error {
	if !m.getLineBusy()  {
		m.log.Print("Can't answer, line isn't ringing (not busy)")
		return ERROR
	}
	if m.offHook() {
		m.log.Print("Can't answer, line off hook already")
		return ERROR
	}

	m.goOffHook()
	time.Sleep(400 * time.Millisecond) // Simulate Carrier Detect delay
	m.raiseCD()
	m.mode = DATAMODE
	m.connect_speed = 38400	// We only go fast...
	return CONNECT
}

// ATZ
// Setup/reset modem.  Leaves RTS & CTS down.
func (m *Modem) reset() error {
	var err error = OK

	m.log.Print("Resetting modem")

	m.goOnHook()
	m.setLineBusy(false)
	m.lowerDSR()
	m.lowerCTS()
	m.lowerRI()
	m.stopTimer()

	m.echoInCmdMode = true  // Echo local keypresses
	m.quiet = false		// Modem offers return status
	m.verbose = true	// Text return codes
	m.volume = 1		// moderate volume
	m.speakermode = 1	// on until other modem heard
	m.lastcmd = ""
	m.lastdialed = ""
	m.connect_speed = 0
	m.connectMsgSpeed = true
	m.busyDetect = true
	m.extendedResultCodes = true
	m.resetRegs()
	m.resetTimer()
	m.addressbook, err = LoadAddressBook()
	if err != nil {
		m.log.Print(err)
	}

	return err
}

// AT&...
// Only support &V for now
func (m *Modem) ampersand(cmd string) error {
	var s string

	if cmd != "&V" {
		return ERROR
	}

	b := func(p bool) (string) {
		if p {
			return"1 "
		}
		return "0 "
	};
	i := func(p int) (string) {
		return fmt.Sprintf("%d ", p)
	};
	x := func(r, b bool) (string) {
		if (r == false && b == false) {
			return "0 "
		}
		if (r == true && b == false) {
			return "1 "
		}
		if (r == true && b == true) {
			return "7 "
		}
		return "0 "
	};

	s += "E" + b(m.echoInCmdMode)
	s += "F1"		// For Hayes 1200 compatability
	s += "L" + i(m.volume)
	s += "M" + i(m.speakermode)
	s += "Q" + b(m.quiet)
	s += "V" + b(m.verbose)
	s += "W" + b(m.connectMsgSpeed)
	s += "X" + x(m.extendedResultCodes, m.busyDetect)
	s += "\n"
	s += m.registers.String()
	m.serial.Println(s)
	return nil
}

// process each command
func (m *Modem) processCommands(commands []string) error {
	var status error
	var cmd string

	m.log.Printf("entering PC: %+v\n", commands)
	status = OK
	for _, cmd = range commands {
		m.log.Printf("Processing: %s", cmd)
		switch cmd[0] {
		case 'A':
			status = m.answer()
		case 'Z':
			status = m.reset()
			time.Sleep(250 * time.Millisecond)
			m.raiseDSR()
			m.raiseCTS()
		case 'E':
			if cmd[1] == '0' {
				m.echoInCmdMode = false
			} else {
				m.echoInCmdMode = true
			}
		case 'F':	// Online Echo mode, F1 assumed for backwards
			        // compatability after Hayes 1200
			status = OK
		case 'H':
			if cmd[1] == '0' {
				status = m.goOnHook()
			} else if cmd[1] == '1' {
				status = m.goOffHook()
			} else {
				status = ERROR
			}
		case 'Q':
			if cmd[1] == '0' {
				m.quiet = true
			} else {
				m.quiet = false
			}
		case 'V':
			if cmd[1] == '0' {
				m.verbose = true
			} else {
				m.verbose = false
			}
		case 'L':
			switch cmd[1] {
			case '0': m.volume = 0
			case '1': m.volume = 1
			case '2': m.volume = 2
			case '3': m.volume = 3
			}
		case 'M':
			switch cmd[1] {
			case '0': m.speakermode = 0
			case '1': m.speakermode = 1
			case '2': m.speakermode = 2
			}
		case 'O':
			m.mode = DATAMODE
			status = OK
		case 'W':
			switch cmd[1] {
			case '0': m.connectMsgSpeed = false
			case '1', '2': m.connectMsgSpeed = true
			}
		case 'X':	// Change result codes displayed
			switch cmd[1] {
			case '0':
				m.extendedResultCodes = false
				m.busyDetect = false
			case '1', '2':
				m.extendedResultCodes = true
				m.busyDetect = false
			case '3', '4', '5', '6', '7':
				m.extendedResultCodes = true
				m.busyDetect = true
			}
		case 'D':
			status = m.dial(cmd)
		case 'S':
			status = m.registerCmd(cmd)
		case '&':
			status = m.ampersand(cmd)
		case '*':
			status = m.debug(cmd)
		default:
			status = ERROR
		}
		if status != OK {
			break
		}
	}
	return status
}

// Helper function to parse non-complex AT commands (everthing except ATS.., ATD...)
func parse(cmd string, opts string) (string, int, error) {

	cmd = strings.ToUpper(cmd)
	if len(cmd) == 1 {
		return cmd + "0", 1, nil
	}

	if strings.ContainsAny(cmd[1:2], opts) {
		return cmd[:2],  2, nil
	}

	return "", 0, fmt.Errorf("Bad command: %s", cmd)
}

// +++
func (m *Modem) command(cmdstring string) {
	var commands []string
	var s, opts string
	var i int
	var status error
	var err error

	// Process here is to parse the entire command string into
	// discrete commands, then execute those discrete commands in
	// the order they were given to us.  This makes syntax
	// checking/failures happen before any commands are executed
	// which is, if I recall correctly, how this works in the real
	// hardware.  Note that the command codes ("DT", "X", etc.)
	// all must be upper case for the rest of the parsing system
	// to work, but the entire command string should be left as it
	// was handed to us.  This is so that we can embed passwords
	// in the extended dial command (ATDE, specifically).

	m.log.Print("command: ", cmdstring)

	if len(cmdstring) < 2  {
		m.log.Print("Cmd too short")
		m.prstatus(ERROR)
		return
	}

	if strings.ToUpper(cmdstring) == "AT" {
		m.prstatus(OK)
		return
	}

	if strings.ToUpper(cmdstring[:2]) != "AT" {
		m.log.Print("Malformed command")
		m.prstatus(ERROR)
		return
	}

	cmd := cmdstring[2:] 		// Skip the 'AT'
	c := 0

	commands = nil
	status = OK
	savecmds := true
	for  c < len(cmd) && status == OK {
		switch (cmd[c]) {
		case 'D', 'd':
			s, i, err = parseDial(cmd[c:])
			if err != nil {
				m.prstatus(ERROR)
				return
			}
			commands = append(commands, s)
			c += i
			continue
		case 'S', 's':
			s, i, err = parseRegisters(cmd[c:])
			if err != nil {
				m.prstatus(ERROR)
				return
			}
			commands = append(commands, s)
			c += i
			continue
		case '*': 	// Custom debug registers
			s, i, err = parseDebug(cmd[c:])
			if err != nil {
				m.prstatus(ERROR)
				return
			}
			commands = append(commands, s)
			c += i
			continue
		case 'A', 'a':
			opts = "0"
		case 'E', 'e', 'H', 'h', 'Q', 'q', 'V', 'v', 'Z', 'z':
			opts = "01"
		case 'L', 'l':
			opts = "0123"
		case 'M', 'm', 'W', 'w':
			opts = "012"
		case 'O', 'o':
			opts = "O"
		case 'X', 'x':
			opts = "01234567"
		case '&':
			opts = "V"
		default:
			m.log.Printf("Unknown command: %s", cmd)
			m.prstatus(ERROR)
			return
		}
		s, i, err = parse(cmd[c:], opts)
		if err != nil {
			m.prstatus(ERROR)
			return
		}
		commands = append(commands, s)
		c += i
	}

	m.log.Print("Command array: %+v", commands)
	status = m.processCommands(commands)
	m.prstatus(status)

	if savecmds && status == OK {
		m.lastcmd = cmdstring
	}
}
<|endoftext|>


<commit_before>package structs

import (
	"errors"
	"fmt"
	"gnd.la/util/stringutil"
	"reflect"
)

var (
	ErrNoStruct = errors.New("not an struct")
	ErrNoFields = errors.New("struct has no fields")
)

type Struct struct {
	// The Struct type
	Type reflect.Type
	// Lists the mangled names of the fields, in order
	MNames []string
	// List the names of the qualified struct fields (e.g. Foo.Bar) in order
	QNames []string
	// Lists the indexes of the members (for FieldByIndex())
	Indexes [][]int
	// Field types, in order
	Types []reflect.Type
	// Field tags, in order
	Tags []*Tag
	// Maps mangled names to indexes
	MNameMap map[string]int
	// Maps qualified names to indexes
	QNameMap map[string]int
	// Lists the field indexes prefix for pointers in embedded structs
	Pointers [][]int
}

// Map takes a qualified struct name and returns its mangled name and type
func (s *Struct) Map(qname string) (string, reflect.Type, error) {
	if n, ok := s.QNameMap[qname]; ok {
		return s.MNames[n], s.Types[n], nil
	}
	return "", nil, fmt.Errorf("can't map field %q to a mangled name", qname)
}

func NewStruct(t interface{}, tags []string) (*Struct, error) {
	var typ reflect.Type
	if tt, ok := t.(reflect.Type); ok {
		typ = tt
	} else {
		typ = reflect.TypeOf(t)
	}
	for typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}
	if typ.Kind() != reflect.Struct {
		return nil, ErrNoStruct
	}
	if typ.NumField() == 0 {
		return nil, ErrNoFields
	}
	s := &Struct{
		Type:     typ,
		MNameMap: make(map[string]int),
		QNameMap: make(map[string]int),
	}
	if err := fields(typ, tags, s, "", "", nil); err != nil {
		return nil, err
	}
	return s, nil
}

func fields(typ reflect.Type, tags []string, s *Struct, qprefix, mprefix string, index []int) error {
	n := typ.NumField()
	for ii := 0; ii < n; ii++ {
		field := typ.Field(ii)
		if field.PkgPath != "" {
			// Unexported
			continue
		}
		ftag := NewTag(field, tags)
		name := ftag.Name()
		if name == "-" {
			// Ignored field
			continue
		}
		if name == "" {
			// Default name
			name = stringutil.CamelCaseToLower(field.Name, "_")
		}
		name = mprefix + name
		if _, ok := s.MNameMap[name]; ok {
			return fmt.Errorf("duplicate field %q in struct %v", name, typ)
		}
		qname := qprefix + field.Name
		// Check type
		ptr := false
		t := field.Type
		for t.Kind() == reflect.Ptr {
			ptr = true
			t = t.Elem()
		}
		if t.Kind() == reflect.Struct && decompose(t, ftag) {
			// Inner struct
			idx := make([]int, len(index))
			copy(idx, index)
			idx = append(idx, field.Index[0])
			prefix := mprefix
			if !ftag.Has("inline") {
				prefix += name + "_"
			}
			err := fields(t, tags, s, qname+".", prefix, idx)
			if err != nil {
				return err
			}
			if ptr {
				s.Pointers = append(s.Pointers, idx)
			}
			continue
		}
		idx := make([]int, len(index))
		copy(idx, index)
		idx = append(idx, field.Index[0])
		s.MNames = append(s.MNames, name)
		s.QNames = append(s.QNames, qname)
		s.Indexes = append(s.Indexes, idx)
		s.Tags = append(s.Tags, ftag)
		s.Types = append(s.Types, t)
		p := len(s.MNames) - 1
		s.MNameMap[name] = p
		s.QNameMap[qname] = p
	}
	return nil
}

// Returns wheter a stuct should decomposed into its fields
func decompose(typ reflect.Type, tag *Tag) bool {
	// TODO: The ORM needs the fields tagged with a codec
	// to not be broken into their members. Make this a
	// parameter, since other users of this function
	// might want all the fields. Make also struct types
	// like time.Time configurable
	return !tag.Has("codec") && !(typ.Name() == "Time" && typ.PkgPath() == "time")
}
<commit_msg>Add Embeds() function<commit_after>package structs

import (
	"errors"
	"fmt"
	"gnd.la/util/stringutil"
	"reflect"
)

var (
	ErrNoStruct = errors.New("not an struct")
	ErrNoFields = errors.New("struct has no fields")
)

type Struct struct {
	// The Struct type
	Type reflect.Type
	// Lists the mangled names of the fields, in order
	MNames []string
	// List the names of the qualified struct fields (e.g. Foo.Bar) in order
	QNames []string
	// Lists the indexes of the members (for FieldByIndex())
	Indexes [][]int
	// Field types, in order
	Types []reflect.Type
	// Field tags, in order
	Tags []*Tag
	// Maps mangled names to indexes
	MNameMap map[string]int
	// Maps qualified names to indexes
	QNameMap map[string]int
	// Lists the field indexes prefix for pointers in embedded structs
	Pointers [][]int
}

// Map takes a qualified struct name and returns its mangled name and type
func (s *Struct) Map(qname string) (string, reflect.Type, error) {
	if n, ok := s.QNameMap[qname]; ok {
		return s.MNames[n], s.Types[n], nil
	}
	return "", nil, fmt.Errorf("can't map field %q to a mangled name", qname)
}

// Embeds returns true iff the struct embeds the given type.
func (s *Struct) Embeds(typ reflect.Type) bool {
	end := s.Type.NumField()
	for ii := 0; ii < end; ii++ {
		field := s.Type.Field(ii)
		if field.Type == typ && field.Anonymous {
			return true
		}
	}
	return false
}

func NewStruct(t interface{}, tags []string) (*Struct, error) {
	var typ reflect.Type
	if tt, ok := t.(reflect.Type); ok {
		typ = tt
	} else {
		typ = reflect.TypeOf(t)
	}
	for typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}
	if typ.Kind() != reflect.Struct {
		return nil, ErrNoStruct
	}
	if typ.NumField() == 0 {
		return nil, ErrNoFields
	}
	s := &Struct{
		Type:     typ,
		MNameMap: make(map[string]int),
		QNameMap: make(map[string]int),
	}
	if err := fields(typ, tags, s, "", "", nil); err != nil {
		return nil, err
	}
	return s, nil
}

func fields(typ reflect.Type, tags []string, s *Struct, qprefix, mprefix string, index []int) error {
	n := typ.NumField()
	for ii := 0; ii < n; ii++ {
		field := typ.Field(ii)
		if field.PkgPath != "" {
			// Unexported
			continue
		}
		ftag := NewTag(field, tags)
		name := ftag.Name()
		if name == "-" {
			// Ignored field
			continue
		}
		if name == "" {
			// Default name
			name = stringutil.CamelCaseToLower(field.Name, "_")
		}
		name = mprefix + name
		if _, ok := s.MNameMap[name]; ok {
			return fmt.Errorf("duplicate field %q in struct %v", name, typ)
		}
		qname := qprefix + field.Name
		// Check type
		ptr := false
		t := field.Type
		for t.Kind() == reflect.Ptr {
			ptr = true
			t = t.Elem()
		}
		if t.Kind() == reflect.Struct && decompose(t, ftag) {
			// Inner struct
			idx := make([]int, len(index))
			copy(idx, index)
			idx = append(idx, field.Index[0])
			prefix := mprefix
			if !ftag.Has("inline") {
				prefix += name + "_"
			}
			err := fields(t, tags, s, qname+".", prefix, idx)
			if err != nil {
				return err
			}
			if ptr {
				s.Pointers = append(s.Pointers, idx)
			}
			continue
		}
		idx := make([]int, len(index))
		copy(idx, index)
		idx = append(idx, field.Index[0])
		s.MNames = append(s.MNames, name)
		s.QNames = append(s.QNames, qname)
		s.Indexes = append(s.Indexes, idx)
		s.Tags = append(s.Tags, ftag)
		s.Types = append(s.Types, t)
		p := len(s.MNames) - 1
		s.MNameMap[name] = p
		s.QNameMap[qname] = p
	}
	return nil
}

// Returns wheter a stuct should decomposed into its fields
func decompose(typ reflect.Type, tag *Tag) bool {
	// TODO: The ORM needs the fields tagged with a codec
	// to not be broken into their members. Make this a
	// parameter, since other users of this function
	// might want all the fields. Make also struct types
	// like time.Time configurable
	return !tag.Has("codec") && !(typ.Name() == "Time" && typ.PkgPath() == "time")
}
<|endoftext|>


<commit_before>package remotecontext // import "github.com/docker/docker/builder/remotecontext"

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/docker/docker/builder"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	"gotest.tools/fs"
)

var binaryContext = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} //xz magic

func TestSelectAcceptableMIME(t *testing.T) {
	validMimeStrings := []string{
		"application/x-bzip2",
		"application/bzip2",
		"application/gzip",
		"application/x-gzip",
		"application/x-xz",
		"application/xz",
		"application/tar",
		"application/x-tar",
		"application/octet-stream",
		"text/plain",
	}

	invalidMimeStrings := []string{
		"",
		"application/octet",
		"application/json",
	}

	for _, m := range invalidMimeStrings {
		if len(selectAcceptableMIME(m)) > 0 {
			t.Fatalf("Should not have accepted %q", m)
		}
	}

	for _, m := range validMimeStrings {
		if str := selectAcceptableMIME(m); str == "" {
			t.Fatalf("Should have accepted %q", m)
		}
	}
}

func TestInspectEmptyResponse(t *testing.T) {
	ct := "application/octet-stream"
	br := ioutil.NopCloser(bytes.NewReader([]byte("")))
	contentType, bReader, err := inspectResponse(ct, br, 0)
	if err == nil {
		t.Fatal("Should have generated an error for an empty response")
	}
	if contentType != "application/octet-stream" {
		t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if len(body) != 0 {
		t.Fatal("response body should remain empty")
	}
}

func TestInspectResponseBinary(t *testing.T) {
	ct := "application/octet-stream"
	br := ioutil.NopCloser(bytes.NewReader(binaryContext))
	contentType, bReader, err := inspectResponse(ct, br, int64(len(binaryContext)))
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "application/octet-stream" {
		t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if len(body) != len(binaryContext) {
		t.Fatalf("Wrong response size %d, should be == len(binaryContext)", len(body))
	}
	for i := range body {
		if body[i] != binaryContext[i] {
			t.Fatalf("Corrupted response body at byte index %d", i)
		}
	}
}

func TestResponseUnsupportedContentType(t *testing.T) {
	content := []byte(dockerfileContents)
	ct := "application/json"
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bReader, err := inspectResponse(ct, br, int64(len(dockerfileContents)))

	if err == nil {
		t.Fatal("Should have returned an error on content-type 'application/json'")
	}
	if contentType != ct {
		t.Fatalf("Should not have altered content-type: orig: %s, altered: %s", ct, contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestInspectResponseTextSimple(t *testing.T) {
	content := []byte(dockerfileContents)
	ct := "text/plain"
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bReader, err := inspectResponse(ct, br, int64(len(content)))
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "text/plain" {
		t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestInspectResponseEmptyContentType(t *testing.T) {
	content := []byte(dockerfileContents)
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bodyReader, err := inspectResponse("", br, int64(len(content)))
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "text/plain" {
		t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bodyReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestUnknownContentLength(t *testing.T) {
	content := []byte(dockerfileContents)
	ct := "text/plain"
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bReader, err := inspectResponse(ct, br, -1)
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "text/plain" {
		t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestDownloadRemote(t *testing.T) {
	contextDir := fs.NewDir(t, "test-builder-download-remote",
		fs.WithFile(builder.DefaultDockerfileName, dockerfileContents))
	defer contextDir.Remove()

	mux := http.NewServeMux()
	server := httptest.NewServer(mux)
	serverURL, _ := url.Parse(server.URL)

	serverURL.Path = "/" + builder.DefaultDockerfileName
	remoteURL := serverURL.String()

	mux.Handle("/", http.FileServer(http.Dir(contextDir.Path())))

	contentType, content, err := downloadRemote(remoteURL)
	assert.NilError(t, err)

	assert.Check(t, is.Equal(mimeTypes.TextPlain, contentType))
	raw, err := ioutil.ReadAll(content)
	assert.NilError(t, err)
	assert.Check(t, is.Equal(dockerfileContents, string(raw)))
}

func TestGetWithStatusError(t *testing.T) {
	var testcases = []struct {
		err          error
		statusCode   int
		expectedErr  string
		expectedBody string
	}{
		{
			statusCode:   200,
			expectedBody: "THE BODY",
		},
		{
			statusCode:   400,
			expectedErr:  "with status 400 Bad Request: broke",
			expectedBody: "broke",
		},
	}
	for _, testcase := range testcases {
		ts := httptest.NewServer(
			http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				buffer := bytes.NewBufferString(testcase.expectedBody)
				w.WriteHeader(testcase.statusCode)
				w.Write(buffer.Bytes())
			}),
		)
		defer ts.Close()
		response, err := GetWithStatusError(ts.URL)

		if testcase.expectedErr == "" {
			assert.NilError(t, err)

			body, err := readBody(response.Body)
			assert.NilError(t, err)
			assert.Check(t, is.Contains(string(body), testcase.expectedBody))
		} else {
			assert.Check(t, is.ErrorContains(err, testcase.expectedErr))
		}
	}
}

func readBody(b io.ReadCloser) ([]byte, error) {
	defer b.Close()
	return ioutil.ReadAll(b)
}
<commit_msg>builder/remotecontext: normalize comment formatting<commit_after>package remotecontext // import "github.com/docker/docker/builder/remotecontext"

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/docker/docker/builder"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	"gotest.tools/fs"
)

var binaryContext = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} // xz magic

func TestSelectAcceptableMIME(t *testing.T) {
	validMimeStrings := []string{
		"application/x-bzip2",
		"application/bzip2",
		"application/gzip",
		"application/x-gzip",
		"application/x-xz",
		"application/xz",
		"application/tar",
		"application/x-tar",
		"application/octet-stream",
		"text/plain",
	}

	invalidMimeStrings := []string{
		"",
		"application/octet",
		"application/json",
	}

	for _, m := range invalidMimeStrings {
		if len(selectAcceptableMIME(m)) > 0 {
			t.Fatalf("Should not have accepted %q", m)
		}
	}

	for _, m := range validMimeStrings {
		if str := selectAcceptableMIME(m); str == "" {
			t.Fatalf("Should have accepted %q", m)
		}
	}
}

func TestInspectEmptyResponse(t *testing.T) {
	ct := "application/octet-stream"
	br := ioutil.NopCloser(bytes.NewReader([]byte("")))
	contentType, bReader, err := inspectResponse(ct, br, 0)
	if err == nil {
		t.Fatal("Should have generated an error for an empty response")
	}
	if contentType != "application/octet-stream" {
		t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if len(body) != 0 {
		t.Fatal("response body should remain empty")
	}
}

func TestInspectResponseBinary(t *testing.T) {
	ct := "application/octet-stream"
	br := ioutil.NopCloser(bytes.NewReader(binaryContext))
	contentType, bReader, err := inspectResponse(ct, br, int64(len(binaryContext)))
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "application/octet-stream" {
		t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if len(body) != len(binaryContext) {
		t.Fatalf("Wrong response size %d, should be == len(binaryContext)", len(body))
	}
	for i := range body {
		if body[i] != binaryContext[i] {
			t.Fatalf("Corrupted response body at byte index %d", i)
		}
	}
}

func TestResponseUnsupportedContentType(t *testing.T) {
	content := []byte(dockerfileContents)
	ct := "application/json"
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bReader, err := inspectResponse(ct, br, int64(len(dockerfileContents)))

	if err == nil {
		t.Fatal("Should have returned an error on content-type 'application/json'")
	}
	if contentType != ct {
		t.Fatalf("Should not have altered content-type: orig: %s, altered: %s", ct, contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestInspectResponseTextSimple(t *testing.T) {
	content := []byte(dockerfileContents)
	ct := "text/plain"
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bReader, err := inspectResponse(ct, br, int64(len(content)))
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "text/plain" {
		t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestInspectResponseEmptyContentType(t *testing.T) {
	content := []byte(dockerfileContents)
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bodyReader, err := inspectResponse("", br, int64(len(content)))
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "text/plain" {
		t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bodyReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestUnknownContentLength(t *testing.T) {
	content := []byte(dockerfileContents)
	ct := "text/plain"
	br := ioutil.NopCloser(bytes.NewReader(content))
	contentType, bReader, err := inspectResponse(ct, br, -1)
	if err != nil {
		t.Fatal(err)
	}
	if contentType != "text/plain" {
		t.Fatalf("Content type should be 'text/plain' but is %q", contentType)
	}
	body, err := ioutil.ReadAll(bReader)
	if err != nil {
		t.Fatal(err)
	}
	if string(body) != dockerfileContents {
		t.Fatalf("Corrupted response body %s", body)
	}
}

func TestDownloadRemote(t *testing.T) {
	contextDir := fs.NewDir(t, "test-builder-download-remote",
		fs.WithFile(builder.DefaultDockerfileName, dockerfileContents))
	defer contextDir.Remove()

	mux := http.NewServeMux()
	server := httptest.NewServer(mux)
	serverURL, _ := url.Parse(server.URL)

	serverURL.Path = "/" + builder.DefaultDockerfileName
	remoteURL := serverURL.String()

	mux.Handle("/", http.FileServer(http.Dir(contextDir.Path())))

	contentType, content, err := downloadRemote(remoteURL)
	assert.NilError(t, err)

	assert.Check(t, is.Equal(mimeTypes.TextPlain, contentType))
	raw, err := ioutil.ReadAll(content)
	assert.NilError(t, err)
	assert.Check(t, is.Equal(dockerfileContents, string(raw)))
}

func TestGetWithStatusError(t *testing.T) {
	var testcases = []struct {
		err          error
		statusCode   int
		expectedErr  string
		expectedBody string
	}{
		{
			statusCode:   200,
			expectedBody: "THE BODY",
		},
		{
			statusCode:   400,
			expectedErr:  "with status 400 Bad Request: broke",
			expectedBody: "broke",
		},
	}
	for _, testcase := range testcases {
		ts := httptest.NewServer(
			http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				buffer := bytes.NewBufferString(testcase.expectedBody)
				w.WriteHeader(testcase.statusCode)
				w.Write(buffer.Bytes())
			}),
		)
		defer ts.Close()
		response, err := GetWithStatusError(ts.URL)

		if testcase.expectedErr == "" {
			assert.NilError(t, err)

			body, err := readBody(response.Body)
			assert.NilError(t, err)
			assert.Check(t, is.Contains(string(body), testcase.expectedBody))
		} else {
			assert.Check(t, is.ErrorContains(err, testcase.expectedErr))
		}
	}
}

func readBody(b io.ReadCloser) ([]byte, error) {
	defer b.Close()
	return ioutil.ReadAll(b)
}
<|endoftext|>


<commit_before>package utils

import (
	"reflect"
	// "github.com/ungerik/go-start/debug"
)

////////////////////////////////////////////////////////////////////////////////
// StructVisitor

type StructVisitor interface {
	BeginStruct(depth int, v reflect.Value) error
	StructField(depth int, v reflect.Value, f reflect.StructField, index int) error
	EndStruct(depth int, v reflect.Value) error

	BeginSlice(depth int, v reflect.Value) error
	SliceField(depth int, v reflect.Value, index int) error
	EndSlice(depth int, v reflect.Value) error

	BeginArray(depth int, v reflect.Value) error
	ArrayField(depth int, v reflect.Value, index int) error
	EndArray(depth int, v reflect.Value) error
}

/*
VisitStruct visits recursively all exported fields of a struct
and reports them via StructVisitor methods.
If a StructVisitor method returns an error, the visitation is aborted
and the error returned as result.
Pointers and interfaces are dereferenced silently until a non nil value
is found.
Structs that are embedded anonymously are inlined so that their fields
are reported as fields of the embedding struct at the same depth.
Anonymous struct fields that are not structs themselves are omitted.
Struct fields with the tag gostart:"-" are ignored.
*/
func VisitStruct(strct interface{}, visitor StructVisitor) error {
	return VisitStructDepth(strct, visitor, -1)
}

/*
VisitStructDepth is identical to VisitStruct except that its recursive
depth is limited to maxDepth with the first depth level being zero.
If maxDepth is -1, then the recursive depth is unlimited (VisitStruct).
*/
func VisitStructDepth(strct interface{}, visitor StructVisitor, maxDepth int) error {
	return visitStructRecursive(reflect.ValueOf(strct), visitor, maxDepth, 0)
}

func visitAnonymousStructFieldRecursive(visitor StructVisitor, v reflect.Value, maxDepth, depth int, index *int) (err error) {
	if v.Kind() == reflect.Struct {
		t := v.Type()
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath == "" && f.Tag.Get("gostart") != "-" { // Only exported fields
				if vi, ok := DereferenceValue(v.Field(i)); ok {
					if f.Anonymous {
						err = visitAnonymousStructFieldRecursive(visitor, vi, maxDepth, depth, index)
						if err != nil {
							return err
						}
					} else {
						err = visitor.StructField(depth, vi, f, *index)
						if err != nil {
							return err
						}
						err = visitStructRecursive(vi, visitor, maxDepth, depth)
						if err != nil {
							return err
						}
						*index++
					}
				}
			}
		}
	}
	return nil
}

func visitStructRecursive(v reflect.Value, visitor StructVisitor, maxDepth, depth int) (err error) {
	if (maxDepth != -1 && depth > maxDepth) || !v.IsValid() {
		return nil
	}

	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		if v.IsNil() {
			return nil
		}
		return visitStructRecursive(v.Elem(), visitor, maxDepth, depth)

	case reflect.Struct:
		err = visitor.BeginStruct(depth, v)
		if err != nil {
			return err
		}
		depth1 := depth + 1
		if maxDepth == -1 || depth1 <= maxDepth {
			t := v.Type()
			index := 0
			for i := 0; i < t.NumField(); i++ {
				f := t.Field(i)
				if f.PkgPath == "" && f.Tag.Get("gostart") != "-" { // Only exported fields
					if vi, ok := DereferenceValue(v.Field(i)); ok {
						if f.Anonymous {
							err = visitAnonymousStructFieldRecursive(visitor, vi, maxDepth, depth1, &index)
							if err != nil {
								return err
							}
						} else {
							err = visitor.StructField(depth1, vi, f, index)
							if err != nil {
								return err
							}
							err = visitStructRecursive(vi, visitor, maxDepth, depth1)
							if err != nil {
								return err
							}
							index++
						}
					}
				}
			}
		}
		return visitor.EndStruct(depth, v)

	case reflect.Slice:
		err = visitor.BeginSlice(depth, v)
		if err != nil {
			return err
		}
		depth1 := depth + 1
		if maxDepth == -1 || depth1 <= maxDepth {
			for i := 0; i < v.Len(); i++ {
				if vi, ok := DereferenceValue(v.Index(i)); ok {
					err = visitor.SliceField(depth1, vi, i)
					if err != nil {
						return err
					}
					err = visitStructRecursive(vi, visitor, maxDepth, depth1)
					if err != nil {
						return err
					}
				}
			}
		}
		return visitor.EndSlice(depth, v)

	case reflect.Array:
		err = visitor.BeginArray(depth, v)
		if err != nil {
			return err
		}
		depth1 := depth + 1
		if maxDepth == -1 || depth1 <= maxDepth {
			for i := 0; i < v.Len(); i++ {
				if vi, ok := DereferenceValue(v.Index(i)); ok {
					err = visitor.ArrayField(depth1, vi, i)
					if err != nil {
						return err
					}
					err = visitStructRecursive(vi, visitor, maxDepth, depth1)
					if err != nil {
						return err
					}
				}
			}
		}
		return visitor.EndArray(depth, v)
	}

	return nil
}

////////////////////////////////////////////////////////////////////////////////
// ModifySliceStructVisitor

// ModifySliceStructVisitor is a StructVisitor that calls its self function
// value in BeginSlice() and ignores all other StructVisitor methos.
// It can be used to modify the length of slices in complex structs.
type ModifySliceStructVisitor func(depth int, v reflect.Value) (reflect.Value, error)

func (self ModifySliceStructVisitor) BeginStruct(depth int, v reflect.Value) error {
	return nil
}

func (self ModifySliceStructVisitor) StructField(depth int, v reflect.Value, f reflect.StructField, index int) error {
	return nil
}

func (self ModifySliceStructVisitor) EndStruct(depth int, v reflect.Value) error {
	return nil
}

func (self ModifySliceStructVisitor) ModifySlice(depth int, v reflect.Value) (reflect.Value, error) {
	return self(depth, v)
}

func (self ModifySliceStructVisitor) BeginSlice(depth int, v reflect.Value) error {
	return nil
}

func (self ModifySliceStructVisitor) SliceField(depth int, v reflect.Value, index int) error {
	return nil
}

func (self ModifySliceStructVisitor) EndSlice(depth int, v reflect.Value) error {
	return nil
}

func (self ModifySliceStructVisitor) BeginArray(depth int, v reflect.Value) error {
	return nil
}

func (self ModifySliceStructVisitor) ArrayField(depth int, v reflect.Value, index int) error {
	return nil
}

func (self ModifySliceStructVisitor) EndArray(depth int, v reflect.Value) error {
	return nil
}
<commit_msg>removed deprecated ModifySliceStructVisitor<commit_after>package utils

import (
	"reflect"
	// "github.com/ungerik/go-start/debug"
)

////////////////////////////////////////////////////////////////////////////////
// StructVisitor

type StructVisitor interface {
	BeginStruct(depth int, v reflect.Value) error
	StructField(depth int, v reflect.Value, f reflect.StructField, index int) error
	EndStruct(depth int, v reflect.Value) error

	BeginSlice(depth int, v reflect.Value) error
	SliceField(depth int, v reflect.Value, index int) error
	EndSlice(depth int, v reflect.Value) error

	BeginArray(depth int, v reflect.Value) error
	ArrayField(depth int, v reflect.Value, index int) error
	EndArray(depth int, v reflect.Value) error
}

/*
VisitStruct visits recursively all exported fields of a struct
and reports them via StructVisitor methods.
If a StructVisitor method returns an error, the visitation is aborted
and the error returned as result.
Pointers and interfaces are dereferenced silently until a non nil value
is found.
Structs that are embedded anonymously are inlined so that their fields
are reported as fields of the embedding struct at the same depth.
Anonymous struct fields that are not structs themselves are omitted.
Struct fields with the tag gostart:"-" are ignored.
*/
func VisitStruct(strct interface{}, visitor StructVisitor) error {
	return VisitStructDepth(strct, visitor, -1)
}

/*
VisitStructDepth is identical to VisitStruct except that its recursive
depth is limited to maxDepth with the first depth level being zero.
If maxDepth is -1, then the recursive depth is unlimited (VisitStruct).
*/
func VisitStructDepth(strct interface{}, visitor StructVisitor, maxDepth int) error {
	return visitStructRecursive(reflect.ValueOf(strct), visitor, maxDepth, 0)
}

func visitAnonymousStructFieldRecursive(visitor StructVisitor, v reflect.Value, maxDepth, depth int, index *int) (err error) {
	if v.Kind() == reflect.Struct {
		t := v.Type()
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath == "" && f.Tag.Get("gostart") != "-" { // Only exported fields
				if vi, ok := DereferenceValue(v.Field(i)); ok {
					if f.Anonymous {
						err = visitAnonymousStructFieldRecursive(visitor, vi, maxDepth, depth, index)
						if err != nil {
							return err
						}
					} else {
						err = visitor.StructField(depth, vi, f, *index)
						if err != nil {
							return err
						}
						err = visitStructRecursive(vi, visitor, maxDepth, depth)
						if err != nil {
							return err
						}
						*index++
					}
				}
			}
		}
	}
	return nil
}

func visitStructRecursive(v reflect.Value, visitor StructVisitor, maxDepth, depth int) (err error) {
	if (maxDepth != -1 && depth > maxDepth) || !v.IsValid() {
		return nil
	}

	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		if v.IsNil() {
			return nil
		}
		return visitStructRecursive(v.Elem(), visitor, maxDepth, depth)

	case reflect.Struct:
		err = visitor.BeginStruct(depth, v)
		if err != nil {
			return err
		}
		depth1 := depth + 1
		if maxDepth == -1 || depth1 <= maxDepth {
			t := v.Type()
			index := 0
			for i := 0; i < t.NumField(); i++ {
				f := t.Field(i)
				if f.PkgPath == "" && f.Tag.Get("gostart") != "-" { // Only exported fields
					if vi, ok := DereferenceValue(v.Field(i)); ok {
						if f.Anonymous {
							err = visitAnonymousStructFieldRecursive(visitor, vi, maxDepth, depth1, &index)
							if err != nil {
								return err
							}
						} else {
							err = visitor.StructField(depth1, vi, f, index)
							if err != nil {
								return err
							}
							err = visitStructRecursive(vi, visitor, maxDepth, depth1)
							if err != nil {
								return err
							}
							index++
						}
					}
				}
			}
		}
		return visitor.EndStruct(depth, v)

	case reflect.Slice:
		err = visitor.BeginSlice(depth, v)
		if err != nil {
			return err
		}
		depth1 := depth + 1
		if maxDepth == -1 || depth1 <= maxDepth {
			for i := 0; i < v.Len(); i++ {
				if vi, ok := DereferenceValue(v.Index(i)); ok {
					err = visitor.SliceField(depth1, vi, i)
					if err != nil {
						return err
					}
					err = visitStructRecursive(vi, visitor, maxDepth, depth1)
					if err != nil {
						return err
					}
				}
			}
		}
		return visitor.EndSlice(depth, v)

	case reflect.Array:
		err = visitor.BeginArray(depth, v)
		if err != nil {
			return err
		}
		depth1 := depth + 1
		if maxDepth == -1 || depth1 <= maxDepth {
			for i := 0; i < v.Len(); i++ {
				if vi, ok := DereferenceValue(v.Index(i)); ok {
					err = visitor.ArrayField(depth1, vi, i)
					if err != nil {
						return err
					}
					err = visitStructRecursive(vi, visitor, maxDepth, depth1)
					if err != nil {
						return err
					}
				}
			}
		}
		return visitor.EndArray(depth, v)
	}

	return nil
}
<|endoftext|>


<commit_before>package vsolver

type SourceManager interface {
	GetProjectInfo(ProjectAtom) (ProjectInfo, error)
	ListVersions(ProjectName) ([]ProjectAtom, error)
	ProjectExists(ProjectName) bool
}

type ProjectManager interface {
	GetProjectInfo() (ProjectInfo, error)
}
<commit_msg>Rough skeleton of SourceManager, ProjectManager<commit_after>package vsolver

import (
	"fmt"
	"sync"

	"github.com/Masterminds/semver"
	"github.com/Masterminds/vcs"
)

type SourceManager interface {
	GetProjectInfo(ProjectAtom) (ProjectInfo, error)
	ListVersions(ProjectName) ([]ProjectAtom, error) // TODO convert return to []Version
	ProjectExists(ProjectName) bool
}

type ProjectManager interface {
	GetInfoAt(Version) (ProjectInfo, error)
	ListVersions() ([]ProjectAtom, error) // TODO convert return to []Version
}

type ProjectAnalyzer interface {
	GetInfo() (ProjectInfo, error)
}

// ExistenceError is a specialized error type that, in addition to the standard
// error interface, also indicates the amount of searching for a project's
// existence that has been performed, and what level of existence has been
// ascertained.
//
// ExistenceErrors should *only* be returned if the (lack of) existence of a
// project was the underling cause of the error.
type ExistenceError interface {
	error
	Existence() (search ProjectExistence, found ProjectExistence)
}

// sourceManager is the default SourceManager for vsolver.
//
// There's no (planned) reason why it would need to be reimplemented by other
// tools; control via dependency injection is intended to be sufficient.
type sourceManager struct {
	cachedir, basedir string
	pms               map[ProjectName]*pmState
	anafac            func(ProjectName) ProjectAnalyzer
	//pme               map[ProjectName]error
}

// Holds a ProjectManager, caches of the managed project's data, and information
// about the freshness of those caches
type pmState struct {
	pm   ProjectManager
	vcur bool // indicates that we've called ListVersions()
	// TODO deal w/ possible local/upstream desync on PAs (e.g., tag moved)
	pas []ProjectAtom // TODO temporary until we have a coherent, overall cache structure
}

func NewSourceManager(cachedir, basedir string) (SourceManager, error) {
	// TODO try to create dir if doesn't exist
	return &sourceManager{
		cachedir: cachedir,
		pms:      make(map[ProjectName]*pmState),
	}, nil

	// TODO drop file lock on cachedir somewhere, here. Caller needs a panic
	// recovery in a defer to be really proper, though
}

type projectInfo struct {
	name     ProjectName
	atominfo map[Version]ProjectInfo // key should be some 'atom' type - a string, i think
	vmap     map[Version]Version     // value is an atom-version, same as above key
}

func (sm *sourceManager) GetProjectInfo(pa ProjectAtom) (ProjectInfo, error) {
	pmc, err := sm.getProjectManager(pa.Name)
	if err != nil {
		return ProjectInfo{}, err
	}

	return pmc.pm.GetInfoAt(pa.Version)
}

func (sm *sourceManager) ListVersions(n ProjectName) ([]ProjectAtom, error) {
	pmc, err := sm.getProjectManager(n)
	if err != nil {
		// TODO More-er proper-er errors
		return nil, err
	}

	if !pmc.vcur {
		pmc.pas, err = pmc.pm.ListVersions()
		// TODO this perhaps-expensively retries in the failure case
		if err != nil {
			pmc.vcur = true
		}
	}

	return pmc.pas, err
}

func (sm *sourceManager) ProjectExists(n ProjectName) bool {
	panic("not implemented")
}

// getProjectManager gets the project manager for the given ProjectName.
//
// If no such manager yet exists, it attempts to create one.
func (sm *sourceManager) getProjectManager(n ProjectName) (*pmState, error) {
	// Check pm cache and errcache first
	if pm, exists := sm.pms[n]; exists {
		return pm, nil
		//} else if pme, errexists := sm.pme[name]; errexists {
		//return nil, pme
	}

	// TODO ensure leading dirs exist
	repo, err := vcs.NewRepo(string(n), fmt.Sprintf("%s/src/%s", sm.cachedir, n))
	if err != nil {
		// TODO be better
		return nil, err
	}

	pm := &projectManager{
		name: n,
		an:   sm.anafac(n),
		repo: repo,
	}

	pms := &pmState{
		pm: pm,
	}
	sm.pms[n] = pms
	return pms, nil
}

type projectManager struct {
	name ProjectName
	mut  sync.RWMutex
	repo vcs.Repo
	ex   ProjectExistence
	an   ProjectAnalyzer
}

func (pm *projectManager) GetInfoAt(v Version) (ProjectInfo, error) {
	pm.mut.Lock()

	err := pm.repo.UpdateVersion(v.Info)
	pm.mut.Unlock()
	if err != nil {
		// TODO More-er proper-er error
		fmt.Println(err)
		panic("canary - why is checkout/whatever failing")
	}

	pm.mut.RLock()
	i, err := pm.an.GetInfo()
	pm.mut.RUnlock()

	return i, err
}

func (pm *projectManager) ListVersions() (atoms []ProjectAtom, err error) {
	pm.mut.Lock()

	// TODO rigorously figure out what the existence level changes here are
	err = pm.repo.Update()
	// Write segment is done, so release write lock
	pm.mut.Unlock()
	if err != nil {
		// TODO More-er proper-er error
		fmt.Println(err)
		panic("canary - why is update failing")
	}

	// And grab a read lock
	pm.mut.RLock()
	defer pm.mut.RUnlock()

	// TODO this is WILDLY inefficient. do better
	tags, err := pm.repo.Tags()
	if err != nil {
		// TODO More-er proper-er error
		fmt.Println(err)
		panic("canary - why is tags failing")
	}

	for _, tag := range tags {
		ci, err := pm.repo.CommitInfo(tag)
		if err != nil {
			// TODO More-er proper-er error
			fmt.Println(err)
			panic("canary - why is commit info failing")
		}

		pa := ProjectAtom{
			Name: pm.name,
		}

		v := Version{
			Type:       V_Version,
			Info:       tag,
			Underlying: ci.Commit,
		}

		sv, err := semver.NewVersion(tag)
		if err != nil {
			v.SemVer = sv
			v.Type = V_Semver
		}

		pa.Version = v
		atoms = append(atoms, pa)
	}

	branches, err := pm.repo.Branches()
	if err != nil {
		// TODO More-er proper-er error
		fmt.Println(err)
		panic("canary - why is branches failing")
	}

	for _, branch := range branches {
		ci, err := pm.repo.CommitInfo(branch)
		if err != nil {
			// TODO More-er proper-er error
			fmt.Println(err)
			panic("canary - why is commit info failing")
		}

		pa := ProjectAtom{
			Name: pm.name,
			Version: Version{
				Type:       V_Branch,
				Info:       branch,
				Underlying: ci.Commit,
			},
		}

		atoms = append(atoms, pa)
	}

	return atoms, nil
}
<|endoftext|>


<commit_before>package sources

import (
	"fmt"
	"github.com/lestrrat/go-libxml2"
	"github.com/lestrrat/go-libxml2/xpath"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

const urlTemplate = "%v/page/%d"

type TumblrSource struct {
	config      TumblrConfig
	imgPath     string
	sizePath    string
	sizePattern *regexp.Regexp
	url         string
	size        int64
}

type TumblrConfig struct {
	ImgPath     string   `json:"img_path"`
	SizePath    string   `json:"size_path"`
	SizePattern string   `json:"size_pattern"`
	Url         string   `json:"url"`
	Tags        []string `json:"tags"`
}

func validXpath(xp string) bool {
	e, err := xpath.NewExpression(xp)
	if err != nil {
		return false
	}
	e.Free()
	return true
}

func NewTumblrSource(config TumblrConfig) (*TumblrSource, error) {
	if !validXpath(config.ImgPath) {
		return nil, fmt.Errorf("Not valid xpath: %s", config.ImgPath)
	}
	if !validXpath(config.SizePath) {
		return nil, fmt.Errorf("Not valid xpath: %s", config.SizePath)
	}

	re, err := regexp.Compile(config.SizePattern)
	if err != nil {
		return nil, fmt.Errorf("Failed to compile regexp: %v", err)
	}

	ts := &TumblrSource{
		config:      config,
		imgPath:     config.ImgPath,
		sizePath:    config.SizePath,
		url:         strings.TrimSuffix(config.Url, "/"),
		sizePattern: re,
		size:        1,
	}

	err = ts.updateSize()
	if err != nil {
		return nil, err
	}
	return ts, nil
}

func (ts *TumblrSource) GetConfig() Config {
	return ts.config
}

func (ts *TumblrSource) GetTags() []string {
	return ts.config.Tags
}

func (ts *TumblrSource) updateSize() error {

	resp, err := http.Get(ts.url)
	if err != nil {
		return fmt.Errorf("Failed to update size: %v", err)
	}
	defer resp.Body.Close()

	doc, err := libxml2.ParseHTMLReader(resp.Body)
	if err != nil {
		return fmt.Errorf("Failed to parse HTML: %v", err)
	}
	defer doc.Free()

	nodes, err := doc.Find(ts.sizePath)
	if err != nil {
		return fmt.Errorf("Failed to apply xpath: %v", err)
	}
	defer nodes.Free()

	node := nodes.NodeList().First()
	if node == nil {
		return fmt.Errorf("Error when getting first node from nodes. Error in config?")
	}
	size, err := ts.parseSize(node.String())
	if err != nil {
		return err
	}
	ts.size = size

	return nil
}

func (ts *TumblrSource) Size() int64 {
	return ts.size
}

func (ts *TumblrSource) GetRandomImage() (string, string, error) {
	pageNumber := rand.Int63n(ts.Size()) + 1 //Page numbers are 1-indexed, and rand is [0-n)
	images, source, err := ts.ListPage(pageNumber)
	if err != nil {
		return "", "", fmt.Errorf("Failed to get image: %v", err)
	}

	imageNumber := rand.Intn(len(images))
	return images[imageNumber], source, nil
}

func (ts *TumblrSource) ListPage(pageNumber int64) ([]string, string, error) {
	retVal := make([]string, 0, 10)
	fullUrl := fmt.Sprintf(urlTemplate, ts.url, pageNumber)
	resp, err := http.Get(fullUrl)
	if err != nil {
		return retVal, fullUrl, err
	}
	defer resp.Body.Close()

	doc, err := libxml2.ParseHTMLReader(resp.Body)
	if err != nil {
		return retVal, fullUrl, fmt.Errorf("Failed to parse HTML: %v", err)
	}
	defer doc.Free()

	nodes, err := doc.Find(ts.imgPath)
	if err != nil {
		return retVal, fullUrl, fmt.Errorf("Failed to apply xpath: %v", err)
	}
	defer nodes.Free()

	it := nodes.NodeIter()
	for it.Next() {
		imgSrc := parseImgSrc(it.Node().String())
		retVal = append(retVal, imgSrc)
	}

	return retVal, fullUrl, nil
}

func parseImgSrc(nodeString string) string {
	parts := strings.Split(nodeString, `"`)
	if len(parts) < 3 {
		return ""
	}
	return parts[1]
}

func (ts *TumblrSource) parseSize(s string) (int64, error) {

	matches := ts.sizePattern.FindStringSubmatch(s)
	if matches == nil {
		return 1, fmt.Errorf("No match for size in \"%s\"", s)
	}
	if len(matches) < 2 {
		return 1, fmt.Errorf("Failed to match size from \"%s\"", s)
	}

	size, err := strconv.ParseInt(matches[1], 10, 64)
	if err != nil {
		return 1, fmt.Errorf("Failed to parse int from \"%s\"\n", matches[1])
	}

	return size, nil
}
<commit_msg>Added source url to error output of Tumblr source<commit_after>package sources

import (
	"fmt"
	"github.com/lestrrat/go-libxml2"
	"github.com/lestrrat/go-libxml2/xpath"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

const urlTemplate = "%v/page/%d"

type TumblrSource struct {
	config      TumblrConfig
	imgPath     string
	sizePath    string
	sizePattern *regexp.Regexp
	url         string
	size        int64
}

type TumblrConfig struct {
	ImgPath     string   `json:"img_path"`
	SizePath    string   `json:"size_path"`
	SizePattern string   `json:"size_pattern"`
	Url         string   `json:"url"`
	Tags        []string `json:"tags"`
}

func validXpath(xp string) bool {
	e, err := xpath.NewExpression(xp)
	if err != nil {
		return false
	}
	e.Free()
	return true
}

func NewTumblrSource(config TumblrConfig) (*TumblrSource, error) {
	if !validXpath(config.ImgPath) {
		return nil, fmt.Errorf("%v: Not valid xpath: %s", config.Url, config.ImgPath)
	}
	if !validXpath(config.SizePath) {
		return nil, fmt.Errorf("%v: Not valid xpath: %s", config.Url, config.SizePath)
	}

	re, err := regexp.Compile(config.SizePattern)
	if err != nil {
		return nil, fmt.Errorf("%v: Failed to compile regexp: %v", config.Url, err)
	}

	ts := &TumblrSource{
		config:      config,
		imgPath:     config.ImgPath,
		sizePath:    config.SizePath,
		url:         strings.TrimSuffix(config.Url, "/"),
		sizePattern: re,
		size:        1,
	}

	err = ts.updateSize()
	if err != nil {
		return nil, err
	}
	return ts, nil
}

func (ts *TumblrSource) GetConfig() Config {
	return ts.config
}

func (ts *TumblrSource) GetTags() []string {
	return ts.config.Tags
}

func (ts *TumblrSource) updateSize() error {

	resp, err := http.Get(ts.url)
	if err != nil {
		return fmt.Errorf("%v: Failed to update size: %v", ts.url, err)
	}
	defer resp.Body.Close()

	doc, err := libxml2.ParseHTMLReader(resp.Body)
	if err != nil {
		return fmt.Errorf("%v: Failed to parse HTML: %v", ts.url, err)
	}
	defer doc.Free()

	nodes, err := doc.Find(ts.sizePath)
	if err != nil {
		return fmt.Errorf("%v: Failed to apply xpath: %v", ts.url, err)
	}
	defer nodes.Free()

	node := nodes.NodeList().First()
	if node == nil {
		return fmt.Errorf("%v: Error when getting first node from nodes. Error in config?", ts.url)
	}
	size, err := ts.parseSize(node.String())
	if err != nil {
		return err
	}
	ts.size = size

	return nil
}

func (ts *TumblrSource) Size() int64 {
	return ts.size
}

func (ts *TumblrSource) GetRandomImage() (string, string, error) {
	pageNumber := rand.Int63n(ts.Size()) + 1 //Page numbers are 1-indexed, and rand is [0-n)
	images, source, err := ts.ListPage(pageNumber)
	if err != nil {
		return "", "", fmt.Errorf("%v: Failed to get image: %v", ts.url, err)
	}

	imageNumber := rand.Intn(len(images))
	return images[imageNumber], source, nil
}

func (ts *TumblrSource) ListPage(pageNumber int64) ([]string, string, error) {
	retVal := make([]string, 0, 10)
	fullUrl := fmt.Sprintf(urlTemplate, ts.url, pageNumber)
	resp, err := http.Get(fullUrl)
	if err != nil {
		return retVal, fullUrl, err
	}
	defer resp.Body.Close()

	doc, err := libxml2.ParseHTMLReader(resp.Body)
	if err != nil {
		return retVal, fullUrl, fmt.Errorf("%v: Failed to parse HTML: %v", ts.url, err)
	}
	defer doc.Free()

	nodes, err := doc.Find(ts.imgPath)
	if err != nil {
		return retVal, fullUrl, fmt.Errorf("%v: Failed to apply xpath: %v", ts.url, err)
	}
	defer nodes.Free()

	it := nodes.NodeIter()
	for it.Next() {
		imgSrc := parseImgSrc(it.Node().String())
		retVal = append(retVal, imgSrc)
	}

	return retVal, fullUrl, nil
}

func parseImgSrc(nodeString string) string {
	parts := strings.Split(nodeString, `"`)
	if len(parts) < 3 {
		return ""
	}
	return parts[1]
}

func (ts *TumblrSource) parseSize(s string) (int64, error) {

	matches := ts.sizePattern.FindStringSubmatch(s)
	if matches == nil {
		return 1, fmt.Errorf("%v: No match for size in \"%s\"", ts.url, s)
	}
	if len(matches) < 2 {
		return 1, fmt.Errorf("%v: Failed to match size from \"%s\"", ts.url, s)
	}

	size, err := strconv.ParseInt(matches[1], 10, 64)
	if err != nil {
		return 1, fmt.Errorf("%v: Failed to parse int from \"%s\"\n", ts.url, matches[1])
	}

	return size, nil
}
<|endoftext|>


<commit_before>package tekton

import (
	"context"
	"fmt"

	pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	fn "knative.dev/kn-plugin-func"
	"knative.dev/kn-plugin-func/builders"
	"knative.dev/kn-plugin-func/buildpacks"
	"knative.dev/kn-plugin-func/s2i"
)

func deletePipelines(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
	client, namespace, err := NewTektonClientAndResolvedNamespace(namespaceOverride)
	if err != nil {
		return
	}

	return client.Pipelines(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
}

func deletePipelineRuns(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
	client, namespace, err := NewTektonClientAndResolvedNamespace(namespaceOverride)
	if err != nil {
		return
	}

	return client.PipelineRuns(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
}

func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipeline {

	// -----  General properties
	pipelineName := getPipelineName(f)

	params := []pplnv1beta1.ParamSpec{
		{
			Name:        "gitRepository",
			Description: "Git repository that hosts the function project",
			Default:     pplnv1beta1.NewArrayOrString(*f.Git.URL),
		},
		{
			Name:        "gitRevision",
			Description: "Git revision to build",
		},
		{
			Name:        "contextDir",
			Description: "Path where the function project is",
			Default:     pplnv1beta1.NewArrayOrString(""),
		},
		{
			Name:        "imageName",
			Description: "Function image name",
		},
		{
			Name:        "builderImage",
			Description: "Builder image to be used",
		},
		{
			Name:        "buildEnvs",
			Description: "Environment variables to set during build time",
			Type:        "array",
		},
	}

	workspaces := []pplnv1beta1.PipelineWorkspaceDeclaration{
		{Name: "source-workspace", Description: "Directory where function source is located."},
		{Name: "dockerconfig-workspace", Description: "Directory containing image registry credentials stored in `config.json` file.", Optional: true},
	}

	var taskBuild pplnv1beta1.PipelineTask

	// Deploy step that uses an image produced by S2I builds needs explicit reference to the image
	referenceImageFromPreviousTaskResults := false

	if f.Builder == builders.Pack {
		// ----- Buildpacks related properties
		workspaces = append(workspaces, pplnv1beta1.PipelineWorkspaceDeclaration{Name: "cache-workspace", Description: "Directory where Buildpacks cache is stored."})
		taskBuild = taskBuildpacks(taskNameFetchSources)

	} else if f.Builder == builders.S2I {
		// ----- S2I build related properties
		taskBuild = taskS2iBuild(taskNameFetchSources)
		referenceImageFromPreviousTaskResults = true
	}

	// ----- Pipeline definition
	tasks := pplnv1beta1.PipelineTaskList{
		taskFetchSources(),
		taskBuild,
		taskDeploy(taskNameBuild, referenceImageFromPreviousTaskResults),
	}

	return &pplnv1beta1.Pipeline{
		ObjectMeta: v1.ObjectMeta{
			Name:   pipelineName,
			Labels: labels,
		},
		Spec: pplnv1beta1.PipelineSpec{
			Params:     params,
			Workspaces: workspaces,
			Tasks:      tasks,
		},
	}
}

func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.PipelineRun {

	// -----  General properties
	revision := ""
	if f.Git.Revision != nil {
		revision = *f.Git.Revision
	}
	contextDir := ""
	if f.Builder == builders.S2I {
		contextDir = "."
	}
	if f.Git.ContextDir != nil {
		contextDir = *f.Git.ContextDir
	}

	buildEnvs := &pplnv1beta1.ArrayOrString{
		Type:     pplnv1beta1.ParamTypeArray,
		ArrayVal: []string{},
	}
	if len(f.BuildEnvs) > 0 {
		var envs []string
		for _, e := range f.BuildEnvs {
			envs = append(envs, e.KeyValuePair())
		}
		buildEnvs.ArrayVal = envs
	}

	params := []pplnv1beta1.Param{
		{
			Name:  "gitRepository",
			Value: *pplnv1beta1.NewArrayOrString(*f.Git.URL),
		},
		{
			Name:  "gitRevision",
			Value: *pplnv1beta1.NewArrayOrString(revision),
		},
		{
			Name:  "contextDir",
			Value: *pplnv1beta1.NewArrayOrString(contextDir),
		},
		{
			Name:  "imageName",
			Value: *pplnv1beta1.NewArrayOrString(f.Image),
		},
		{
			Name:  "builderImage",
			Value: *pplnv1beta1.NewArrayOrString(getBuilderImage(f)),
		},
		{
			Name:  "buildEnvs",
			Value: *buildEnvs,
		},
	}

	workspaces := []pplnv1beta1.WorkspaceBinding{
		{
			Name: "source-workspace",
			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
				ClaimName: getPipelinePvcName(f),
			},
			SubPath: "source",
		},
		{
			Name: "dockerconfig-workspace",
			Secret: &corev1.SecretVolumeSource{
				SecretName: getPipelineSecretName(f),
			},
		},
	}

	if f.Builder == builders.Pack {
		// ----- Buildpacks related properties

		workspaces = append(workspaces, pplnv1beta1.WorkspaceBinding{
			Name: "cache-workspace",
			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
				ClaimName: getPipelinePvcName(f),
			},
			SubPath: "cache",
		})
	}

	// ----- PipelineRun definition
	return &pplnv1beta1.PipelineRun{
		ObjectMeta: v1.ObjectMeta{
			GenerateName: fmt.Sprintf("%s-run-", getPipelineName(f)),
			Labels:       labels,
		},
		Spec: pplnv1beta1.PipelineRunSpec{
			PipelineRef: &pplnv1beta1.PipelineRef{
				Name: getPipelineName(f),
			},
			Params:     params,
			Workspaces: workspaces,
		},
	}
}

// guilderImage returns the builder image to use when building the Function
// with the Pack strategy if it can be calculated (the Function has a defined
// language runtime.  Errors are checked elsewhere, so at this level they
// manifest as an inability to get a builder image = empty string.
func getBuilderImage(f fn.Function) (name string) {
	if f.Builder == builders.S2I {
		name, _ = s2i.BuilderImage(f, builders.S2I)
	} else {
		name, _ = buildpacks.BuilderImage(f, builders.Pack)
	}
	return
}

func getPipelineName(f fn.Function) string {
	return fmt.Sprintf("%s-%s-%s-pipeline", f.Name, f.BuildType, f.Builder)
}

func getPipelineSecretName(f fn.Function) string {
	return fmt.Sprintf("%s-secret", getPipelineName(f))
}

func getPipelinePvcName(f fn.Function) string {
	return fmt.Sprintf("%s-pvc", getPipelineName(f))
}
<commit_msg>fix: on cluster build - workaround for Tekton issue with empty array (#1214)<commit_after>package tekton

import (
	"context"
	"fmt"

	pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	fn "knative.dev/kn-plugin-func"
	"knative.dev/kn-plugin-func/builders"
	"knative.dev/kn-plugin-func/buildpacks"
	"knative.dev/kn-plugin-func/s2i"
)

func deletePipelines(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
	client, namespace, err := NewTektonClientAndResolvedNamespace(namespaceOverride)
	if err != nil {
		return
	}

	return client.Pipelines(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
}

func deletePipelineRuns(ctx context.Context, namespaceOverride string, listOptions metav1.ListOptions) (err error) {
	client, namespace, err := NewTektonClientAndResolvedNamespace(namespaceOverride)
	if err != nil {
		return
	}

	return client.PipelineRuns(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
}

func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipeline {

	// -----  General properties
	pipelineName := getPipelineName(f)

	params := []pplnv1beta1.ParamSpec{
		{
			Name:        "gitRepository",
			Description: "Git repository that hosts the function project",
			Default:     pplnv1beta1.NewArrayOrString(*f.Git.URL),
		},
		{
			Name:        "gitRevision",
			Description: "Git revision to build",
		},
		{
			Name:        "contextDir",
			Description: "Path where the function project is",
			Default:     pplnv1beta1.NewArrayOrString(""),
		},
		{
			Name:        "imageName",
			Description: "Function image name",
		},
		{
			Name:        "builderImage",
			Description: "Builder image to be used",
		},
		{
			Name:        "buildEnvs",
			Description: "Environment variables to set during build time",
			Type:        "array",
		},
	}

	workspaces := []pplnv1beta1.PipelineWorkspaceDeclaration{
		{Name: "source-workspace", Description: "Directory where function source is located."},
		{Name: "dockerconfig-workspace", Description: "Directory containing image registry credentials stored in `config.json` file.", Optional: true},
	}

	var taskBuild pplnv1beta1.PipelineTask

	// Deploy step that uses an image produced by S2I builds needs explicit reference to the image
	referenceImageFromPreviousTaskResults := false

	if f.Builder == builders.Pack {
		// ----- Buildpacks related properties
		workspaces = append(workspaces, pplnv1beta1.PipelineWorkspaceDeclaration{Name: "cache-workspace", Description: "Directory where Buildpacks cache is stored."})
		taskBuild = taskBuildpacks(taskNameFetchSources)

	} else if f.Builder == builders.S2I {
		// ----- S2I build related properties
		taskBuild = taskS2iBuild(taskNameFetchSources)
		referenceImageFromPreviousTaskResults = true
	}

	// ----- Pipeline definition
	tasks := pplnv1beta1.PipelineTaskList{
		taskFetchSources(),
		taskBuild,
		taskDeploy(taskNameBuild, referenceImageFromPreviousTaskResults),
	}

	return &pplnv1beta1.Pipeline{
		ObjectMeta: v1.ObjectMeta{
			Name:   pipelineName,
			Labels: labels,
		},
		Spec: pplnv1beta1.PipelineSpec{
			Params:     params,
			Workspaces: workspaces,
			Tasks:      tasks,
		},
	}
}

func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.PipelineRun {

	// -----  General properties
	revision := ""
	if f.Git.Revision != nil {
		revision = *f.Git.Revision
	}
	contextDir := ""
	if f.Builder == builders.S2I {
		contextDir = "."
	}
	if f.Git.ContextDir != nil {
		contextDir = *f.Git.ContextDir
	}

	buildEnvs := &pplnv1beta1.ArrayOrString{
		Type:     pplnv1beta1.ParamTypeArray,
		ArrayVal: []string{},
	}
	if len(f.BuildEnvs) > 0 {
		var envs []string
		for _, e := range f.BuildEnvs {
			envs = append(envs, e.KeyValuePair())
		}
		buildEnvs.ArrayVal = envs
	} else {
		// need to hack empty BuildEnvs array on Tekton v0.39.0+
		// until https://github.com/tektoncd/pipeline/issues/5149 is resolved and released
		buildEnvs.ArrayVal = append(buildEnvs.ArrayVal, "=")
	}

	params := []pplnv1beta1.Param{
		{
			Name:  "gitRepository",
			Value: *pplnv1beta1.NewArrayOrString(*f.Git.URL),
		},
		{
			Name:  "gitRevision",
			Value: *pplnv1beta1.NewArrayOrString(revision),
		},
		{
			Name:  "contextDir",
			Value: *pplnv1beta1.NewArrayOrString(contextDir),
		},
		{
			Name:  "imageName",
			Value: *pplnv1beta1.NewArrayOrString(f.Image),
		},
		{
			Name:  "builderImage",
			Value: *pplnv1beta1.NewArrayOrString(getBuilderImage(f)),
		},
		{
			Name:  "buildEnvs",
			Value: *buildEnvs,
		},
	}

	workspaces := []pplnv1beta1.WorkspaceBinding{
		{
			Name: "source-workspace",
			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
				ClaimName: getPipelinePvcName(f),
			},
			SubPath: "source",
		},
		{
			Name: "dockerconfig-workspace",
			Secret: &corev1.SecretVolumeSource{
				SecretName: getPipelineSecretName(f),
			},
		},
	}

	if f.Builder == builders.Pack {
		// ----- Buildpacks related properties

		workspaces = append(workspaces, pplnv1beta1.WorkspaceBinding{
			Name: "cache-workspace",
			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
				ClaimName: getPipelinePvcName(f),
			},
			SubPath: "cache",
		})
	}

	// ----- PipelineRun definition
	return &pplnv1beta1.PipelineRun{
		ObjectMeta: v1.ObjectMeta{
			GenerateName: fmt.Sprintf("%s-run-", getPipelineName(f)),
			Labels:       labels,
		},
		Spec: pplnv1beta1.PipelineRunSpec{
			PipelineRef: &pplnv1beta1.PipelineRef{
				Name: getPipelineName(f),
			},
			Params:     params,
			Workspaces: workspaces,
		},
	}
}

// guilderImage returns the builder image to use when building the Function
// with the Pack strategy if it can be calculated (the Function has a defined
// language runtime.  Errors are checked elsewhere, so at this level they
// manifest as an inability to get a builder image = empty string.
func getBuilderImage(f fn.Function) (name string) {
	if f.Builder == builders.S2I {
		name, _ = s2i.BuilderImage(f, builders.S2I)
	} else {
		name, _ = buildpacks.BuilderImage(f, builders.Pack)
	}
	return
}

func getPipelineName(f fn.Function) string {
	return fmt.Sprintf("%s-%s-%s-pipeline", f.Name, f.BuildType, f.Builder)
}

func getPipelineSecretName(f fn.Function) string {
	return fmt.Sprintf("%s-secret", getPipelineName(f))
}

func getPipelinePvcName(f fn.Function) string {
	return fmt.Sprintf("%s-pvc", getPipelineName(f))
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. 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 api_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"reflect"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/fsouza/go-dockerclient"
	"github.com/google/gofuzz"
)

var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.")

// apiObjectFuzzer can randomly populate api objects.
var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
	func(j *runtime.PluginBase, c fuzz.Continue) {
		// Do nothing; this struct has only a Kind field and it must stay blank in memory.
	},
	func(j *runtime.JSONBase, c fuzz.Continue) {
		// We have to customize the randomization of JSONBases because their
		// APIVersion and Kind must remain blank in memory.
		j.APIVersion = ""
		j.Kind = ""
		j.ID = c.RandString()
		// TODO: Fix JSON/YAML packages and/or write custom encoding
		// for uint64's. Somehow the LS *byte* of this is lost, but
		// only when all 8 bytes are set.
		j.ResourceVersion = c.RandUint64() >> 8
		j.SelfLink = c.RandString()

		var sec, nsec int64
		c.Fuzz(&sec)
		c.Fuzz(&nsec)
		j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
	},
	func(j *api.JSONBase, c fuzz.Continue) {
		// We have to customize the randomization of JSONBases because their
		// APIVersion and Kind must remain blank in memory.
		j.APIVersion = ""
		j.Kind = ""
		j.ID = c.RandString()
		// TODO: Fix JSON/YAML packages and/or write custom encoding
		// for uint64's. Somehow the LS *byte* of this is lost, but
		// only when all 8 bytes are set.
		j.ResourceVersion = c.RandUint64() >> 8
		j.SelfLink = c.RandString()

		var sec, nsec int64
		c.Fuzz(&sec)
		c.Fuzz(&nsec)
		j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
	},
	func(intstr *util.IntOrString, c fuzz.Continue) {
		// util.IntOrString will panic if its kind is set wrong.
		if c.RandBool() {
			intstr.Kind = util.IntstrInt
			intstr.IntVal = int(c.RandUint64())
			intstr.StrVal = ""
		} else {
			intstr.Kind = util.IntstrString
			intstr.IntVal = 0
			intstr.StrVal = c.RandString()
		}
	},
	func(u64 *uint64, c fuzz.Continue) {
		// TODO: uint64's are NOT handled right.
		*u64 = c.RandUint64() >> 8
	},
	func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) {
		// This is necessary because keys with nil values get omitted.
		// TODO: Is this a bug?
		pb[docker.Port(c.RandString())] = []docker.PortBinding{
			{c.RandString(), c.RandString()},
			{c.RandString(), c.RandString()},
		}
	},
	func(pm map[string]docker.PortMapping, c fuzz.Continue) {
		// This is necessary because keys with nil values get omitted.
		// TODO: Is this a bug?
		pm[c.RandString()] = docker.PortMapping{
			c.RandString(): c.RandString(),
		}
	},
)

func objDiff(a, b runtime.Object) string {
	ab, err := json.Marshal(a)
	if err != nil {
		panic("a")
	}
	bb, err := json.Marshal(b)
	if err != nil {
		panic("b")
	}
	return util.StringDiff(string(ab), string(bb))

	// An alternate diff attempt, in case json isn't showing you
	// the difference. (reflect.DeepEqual makes a distinction between
	// nil and empty slices, for example.)
	return util.StringDiff(
		fmt.Sprintf("%#v", a),
		fmt.Sprintf("%#v", b),
	)
}

func runTest(t *testing.T, source runtime.Object) {
	name := reflect.TypeOf(source).Elem().Name()
	apiObjectFuzzer.Fuzz(source)
	j, err := runtime.FindJSONBase(source)
	if err != nil {
		t.Fatalf("Unexpected error %v for %#v", err, source)
	}
	j.SetKind("")
	j.SetAPIVersion("")

	data, err := runtime.DefaultCodec.Encode(source)
	if err != nil {
		t.Errorf("%v: %v (%#v)", name, err, source)
		return
	}

	obj2, err := runtime.DefaultCodec.Decode(data)
	if err != nil {
		t.Errorf("%v: %v", name, err)
		return
	} else {
		if !reflect.DeepEqual(source, obj2) {
			t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2))
			return
		}
	}
	obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object)
	err = runtime.DefaultCodec.DecodeInto(data, obj3)
	if err != nil {
		t.Errorf("2: %v: %v", name, err)
		return
	} else {
		if !reflect.DeepEqual(source, obj3) {
			t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3))
			return
		}
	}
}

func TestTypes(t *testing.T) {
	table := []runtime.Object{
		&api.PodList{},
		&api.Pod{},
		&api.ServiceList{},
		&api.Service{},
		&api.ReplicationControllerList{},
		&api.ReplicationController{},
		&api.MinionList{},
		&api.Minion{},
		&api.Status{},
		&api.ServerOpList{},
		&api.ServerOp{},
		&api.ContainerManifestList{},
		&api.Endpoints{},
		&api.Binding{},
	}
	for _, item := range table {
		// Try a few times, since runTest uses random values.
		for i := 0; i < *fuzzIters; i++ {
			runTest(t, item)
		}
	}
}

func TestEncode_Ptr(t *testing.T) {
	pod := &api.Pod{
		Labels: map[string]string{"name": "foo"},
	}
	obj := runtime.Object(pod)
	data, err := runtime.DefaultCodec.Encode(obj)
	obj2, err2 := runtime.DefaultCodec.Decode(data)
	if err != nil || err2 != nil {
		t.Fatalf("Failure: '%v' '%v'", err, err2)
	}
	if _, ok := obj2.(*api.Pod); !ok {
		t.Fatalf("Got wrong type")
	}
	if !reflect.DeepEqual(obj2, pod) {
		t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
	}
}

func TestBadJSONRejection(t *testing.T) {
	badJSONMissingKind := []byte(`{ }`)
	if _, err := runtime.DefaultCodec.Decode(badJSONMissingKind); err == nil {
		t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
	}
	badJSONUnknownType := []byte(`{"kind": "bar"}`)
	if _, err1 := runtime.DefaultCodec.Decode(badJSONUnknownType); err1 == nil {
		t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
	}
	/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
	if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil {
		t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
	}*/
}
<commit_msg>Simple refactor for ease of readability<commit_after>/*
Copyright 2014 Google Inc. 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 api_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"reflect"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/fsouza/go-dockerclient"
	"github.com/google/gofuzz"
)

var fuzzIters = flag.Int("fuzz_iters", 50, "How many fuzzing iterations to do.")

// apiObjectFuzzer can randomly populate api objects.
var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
	func(j *runtime.PluginBase, c fuzz.Continue) {
		// Do nothing; this struct has only a Kind field and it must stay blank in memory.
	},
	func(j *runtime.JSONBase, c fuzz.Continue) {
		// We have to customize the randomization of JSONBases because their
		// APIVersion and Kind must remain blank in memory.
		j.APIVersion = ""
		j.Kind = ""
		j.ID = c.RandString()
		// TODO: Fix JSON/YAML packages and/or write custom encoding
		// for uint64's. Somehow the LS *byte* of this is lost, but
		// only when all 8 bytes are set.
		j.ResourceVersion = c.RandUint64() >> 8
		j.SelfLink = c.RandString()

		var sec, nsec int64
		c.Fuzz(&sec)
		c.Fuzz(&nsec)
		j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
	},
	func(j *api.JSONBase, c fuzz.Continue) {
		// We have to customize the randomization of JSONBases because their
		// APIVersion and Kind must remain blank in memory.
		j.APIVersion = ""
		j.Kind = ""
		j.ID = c.RandString()
		// TODO: Fix JSON/YAML packages and/or write custom encoding
		// for uint64's. Somehow the LS *byte* of this is lost, but
		// only when all 8 bytes are set.
		j.ResourceVersion = c.RandUint64() >> 8
		j.SelfLink = c.RandString()

		var sec, nsec int64
		c.Fuzz(&sec)
		c.Fuzz(&nsec)
		j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
	},
	func(intstr *util.IntOrString, c fuzz.Continue) {
		// util.IntOrString will panic if its kind is set wrong.
		if c.RandBool() {
			intstr.Kind = util.IntstrInt
			intstr.IntVal = int(c.RandUint64())
			intstr.StrVal = ""
		} else {
			intstr.Kind = util.IntstrString
			intstr.IntVal = 0
			intstr.StrVal = c.RandString()
		}
	},
	func(u64 *uint64, c fuzz.Continue) {
		// TODO: uint64's are NOT handled right.
		*u64 = c.RandUint64() >> 8
	},
	func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) {
		// This is necessary because keys with nil values get omitted.
		// TODO: Is this a bug?
		pb[docker.Port(c.RandString())] = []docker.PortBinding{
			{c.RandString(), c.RandString()},
			{c.RandString(), c.RandString()},
		}
	},
	func(pm map[string]docker.PortMapping, c fuzz.Continue) {
		// This is necessary because keys with nil values get omitted.
		// TODO: Is this a bug?
		pm[c.RandString()] = docker.PortMapping{
			c.RandString(): c.RandString(),
		}
	},
)

func objDiff(a, b runtime.Object) string {
	ab, err := json.Marshal(a)
	if err != nil {
		panic("a")
	}
	bb, err := json.Marshal(b)
	if err != nil {
		panic("b")
	}
	return util.StringDiff(string(ab), string(bb))

	// An alternate diff attempt, in case json isn't showing you
	// the difference. (reflect.DeepEqual makes a distinction between
	// nil and empty slices, for example.)
	return util.StringDiff(
		fmt.Sprintf("%#v", a),
		fmt.Sprintf("%#v", b),
	)
}

func runTest(t *testing.T, source runtime.Object) {
	name := reflect.TypeOf(source).Elem().Name()
	apiObjectFuzzer.Fuzz(source)
	j, err := runtime.FindJSONBase(source)
	if err != nil {
		t.Fatalf("Unexpected error %v for %#v", err, source)
	}
	j.SetKind("")
	j.SetAPIVersion("")

	data, err := latest.Codec.Encode(source)
	if err != nil {
		t.Errorf("%v: %v (%#v)", name, err, source)
		return
	}

	obj2, err := latest.Codec.Decode(data)
	if err != nil {
		t.Errorf("%v: %v", name, err)
		return
	} else {
		if !reflect.DeepEqual(source, obj2) {
			t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2))
			return
		}
	}
	obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object)
	err = latest.Codec.DecodeInto(data, obj3)
	if err != nil {
		t.Errorf("2: %v: %v", name, err)
		return
	} else {
		if !reflect.DeepEqual(source, obj3) {
			t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3))
			return
		}
	}
}

func TestTypes(t *testing.T) {
	table := []runtime.Object{
		&api.PodList{},
		&api.Pod{},
		&api.ServiceList{},
		&api.Service{},
		&api.ReplicationControllerList{},
		&api.ReplicationController{},
		&api.MinionList{},
		&api.Minion{},
		&api.Status{},
		&api.ServerOpList{},
		&api.ServerOp{},
		&api.ContainerManifestList{},
		&api.Endpoints{},
		&api.Binding{},
	}
	for _, item := range table {
		// Try a few times, since runTest uses random values.
		for i := 0; i < *fuzzIters; i++ {
			runTest(t, item)
		}
	}
}

func TestEncode_Ptr(t *testing.T) {
	pod := &api.Pod{
		Labels: map[string]string{"name": "foo"},
	}
	obj := runtime.Object(pod)
	data, err := latest.Codec.Encode(obj)
	obj2, err2 := latest.Codec.Decode(data)
	if err != nil || err2 != nil {
		t.Fatalf("Failure: '%v' '%v'", err, err2)
	}
	if _, ok := obj2.(*api.Pod); !ok {
		t.Fatalf("Got wrong type")
	}
	if !reflect.DeepEqual(obj2, pod) {
		t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
	}
}

func TestBadJSONRejection(t *testing.T) {
	badJSONMissingKind := []byte(`{ }`)
	if _, err := latest.Codec.Decode(badJSONMissingKind); err == nil {
		t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
	}
	badJSONUnknownType := []byte(`{"kind": "bar"}`)
	if _, err1 := latest.Codec.Decode(badJSONUnknownType); err1 == nil {
		t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
	}
	/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
	if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil {
		t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
	}*/
}
<|endoftext|>