<commit_before>// Package buffer provides unbounded, file-backed rune buffers.
package buffer

import (
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"strconv"
)

// RuneBytes is the number of bytes in Go's rune type.
const runeBytes = 4

// A Runes is an unbounded rune buffer backed by a file.
type Runes struct {
	// F is the file that backs the buffer. It is created lazily.
	f *os.File
	// BlockSize is the maximum number of runes in a block.
	blockSize int
	// Blocks contains all blocks of the buffer in order.
	// Free contains blocks that are free to be re-allocated.
	blocks, free []block
	// End is the byte offset of the end of the backing file.
	end int64

	// Cache is the index of the block whose data is currently cached.
	cached int
	// Cache is the cached data.
	cache []rune
	// Dirty tracks whether the cached data has changed since it was read.
	dirty bool

	// Size is the number of runes in the buffer.
	size int64
}

// A block describes a portion of the buffer and its location in the backing file.
type block struct {
	// Start is the byte offset of the block in the file.
	start int64
	// N is the number of runes in the block.
	n int
}

// New returns a new, empty buffer.
// No more than blockSize runes are cached in memory.
func New(blockSize int) *Runes {
	return &Runes{
		blockSize: blockSize,
		cached:    -1,
		cache:     make([]rune, blockSize),
	}
}

// Close closes the buffer and removes it's backing file.
func (b *Runes) Close() error {
	b.cache = nil
	if b.f == nil {
		return nil
	}
	path := b.f.Name()
	if err := b.f.Close(); err != nil {
		return err
	}
	return os.Remove(path)
}

// Size returns the number of runes in the buffer.
func (b *Runes) Size() int64 { return b.size }

// A RuneReadError is paniced if Rune encounters an error
// reading a rune that is in the bounds of the buffer.
type RuneReadError struct{ error }

// RecoverRuneReadError recovers a RuneReadError,
// setting the error pointed to by err to the error.
// If the recovered error is not of type RuneReadError,
// then it is re-paniced.
// This function is intended to be called in a defer statement
// with err as the address of a named error return.
func RecoverRuneReadError(err *error) {
	switch e := recover().(type) {
	case nil:
		return
	case RuneReadError:
		*err = e.error
	default:
		panic(e)
	}
}

// Rune returns the rune at the given offset.
// If the rune is out of range it panics.
// If there is an error reading, it panics a RuneReadError containing the error.
func (b *Runes) Rune(offs int64) rune {
	if offs < 0 || offs > b.Size() {
		panic("rune index out of bounds")
	}
	i, q0 := b.blockAt(offs)
	if _, err := b.get(i); err != nil {
		panic(RuneReadError{err})
	}
	return b.cache[offs-q0]
}

// Read reads runes from the buffer beginning at a given offset.
// The return value is the number of runes read.
// If fewer than len(rs) runes are read then the error states why.
// If the offset is beyond the end of the buffer, 0 and io.EOF are returned.
func (b *Runes) Read(rs []rune, offs int64) (int, error) {
	switch {
	case offs < 0:
		return 0, errors.New("invalid offset: " + strconv.FormatInt(offs, 10))
	case offs == b.Size() && len(rs) == 0:
		return 0, nil
	case offs >= b.Size():
		return 0, io.EOF
	}
	var tot int
	for len(rs) > 0 {
		if offs == b.Size() {
			return tot, io.EOF
		}
		i, q0 := b.blockAt(offs)
		blk, err := b.get(i)
		if err != nil {
			return tot, err
		}
		o := int(offs - q0)
		m := copy(rs, b.cache[o:blk.n])
		rs = rs[m:]
		offs += int64(m)
		tot += m
	}
	return tot, nil
}

// Insert inserts runes into the buffer at the given offset..
// The return value is the number of runes added and any error that was encountered.
// It is an error to add at a negative offset or an offset beyond the buffer size.
func (b *Runes) Insert(rs []rune, offs int64) (int, error) {
	if offs < 0 || offs > b.Size() {
		return 0, errors.New("invalid offset: " + strconv.FormatInt(offs, 10))
	}
	var tot int
	for len(rs) > 0 {
		i, q0 := b.blockAt(offs)
		blk, err := b.get(i)
		if err != nil {
			return tot, err
		}
		m := b.blockSize - blk.n
		if m == 0 {
			if i, err = b.insertAt(offs); err != nil {
				return tot, err
			}
			if blk, err = b.get(i); err != nil {
				return tot, err
			}
			q0 = offs
			m = b.blockSize
		}
		if m > len(rs) {
			m = len(rs)
		}
		o := int(offs - q0)
		copy(b.cache[o+m:], b.cache[o:blk.n])
		copy(b.cache[o:], rs[:m])
		b.dirty = true
		rs = rs[m:]
		blk.n += m
		b.size += int64(m)
		offs += int64(m)
		tot += m
	}
	return tot, nil
}

// Delete deletes runes from the buffer starting at the given offset.
// The return value is the number of runes deleted.
// If fewer than n runes are deleted, the error states why.
func (b *Runes) Delete(n, offs int64) (int64, error) {
	if n < 0 {
		panic("bad count: " + strconv.FormatInt(n, 10))
	}
	if offs < 0 || offs+n > b.Size() {
		return 0, errors.New("invalid offset: " + strconv.FormatInt(offs, 10))
	}
	var tot int64
	for n > 0 {
		i, q0 := b.blockAt(offs)
		blk, err := b.get(i)
		if err != nil {
			return tot, err
		}
		o := int(offs - q0)
		m := blk.n - o
		if int64(m) > n {
			m = int(n)
		}
		if o == 0 && n >= int64(blk.n) {
			// Remove the entire block.
			b.freeBlock(*blk)
			b.blocks = append(b.blocks[:i], b.blocks[i+1:]...)
			b.cached = -1
		} else {
			// Remove a portion of the block.
			copy(b.cache[o:], b.cache[o+m:])
			b.dirty = true
			blk.n -= m
		}
		n -= int64(m)
		tot += int64(m)
		b.size -= int64(m)
	}
	return tot, nil
}

func (b *Runes) allocBlock() block {
	if l := len(b.free); l > 0 {
		blk := b.free[l-1]
		b.free = b.free[:l-1]
		return blk
	}
	blk := block{start: b.end}
	b.end += int64(b.blockSize * runeBytes)
	return blk
}

func (b *Runes) freeBlock(blk block) {
	b.free = append(b.free, block{start: blk.start})
}

// BlockAt returns the index and start address of the block containing the address.
// If the address is one beyond the end of the file, a new block is allocated.
// BlockAt panics if the address is negative or more than one past the end.
func (b *Runes) blockAt(at int64) (int, int64) {
	if at < 0 || at > b.Size() {
		panic("invalid offset: " + strconv.FormatInt(at, 10))
	}
	if at == b.Size() {
		i := len(b.blocks)
		blk := b.allocBlock()
		b.blocks = append(b.blocks[:i], append([]block{blk}, b.blocks[i:]...)...)
		return i, at
	}
	var q0 int64
	for i, blk := range b.blocks {
		if q0 <= at && at < q0+int64(blk.n) {
			return i, q0
		}
		q0 += int64(blk.n)
	}
	panic("impossible")
}

// insertAt inserts a block at the address and returns the new block's index.
// If a block contains the address then it is split.
func (b *Runes) insertAt(at int64) (int, error) {
	i, q0 := b.blockAt(at)
	o := int(at - q0)
	blk := b.blocks[i]
	if at == q0 {
		// Adding immediately before blk, no need to split.
		nblk := b.allocBlock()
		b.blocks = append(b.blocks[:i], append([]block{nblk}, b.blocks[i:]...)...)
		if b.cached == i {
			b.cached = i + 1
		}
		return i, nil
	}

	// Splitting blk.
	// Make sure it's both on disk and in the cache.
	if b.cached == i && b.dirty {
		if err := b.put(); err != nil {
			return -1, err
		}
	} else if _, err := b.get(i); err != nil {
		return -1, err
	}

	// Resize blk.
	b.blocks[i].n = int(o)

	// Insert the new, empty block.
	nblk := b.allocBlock()
	b.blocks = append(b.blocks[:i+1], append([]block{nblk}, b.blocks[i+1:]...)...)

	// Allocate a block for the second half of blk and set it as the cache.
	// The next put will write it out.
	nblk = b.allocBlock()
	b.blocks = append(b.blocks[:i+2], append([]block{nblk}, b.blocks[i+2:]...)...)
	b.blocks[i+2].n = blk.n - o
	copy(b.cache, b.cache[o:])
	b.cached = i + 2
	b.dirty = true

	return i + 1, nil
}

// File returns an *os.File, creating a new file if one is not created yet.
func (b *Runes) file() (*os.File, error) {
	if b.f == nil {
		f, err := ioutil.TempFile(os.TempDir(), "edit")
		if err != nil {
			return nil, err
		}
		b.f = f
	}
	return b.f, nil
}

// Put writes the cached block back to the file.
func (b *Runes) put() error {
	if b.cached < 0 || !b.dirty || len(b.cache) == 0 {
		return nil
	}
	blk := b.blocks[b.cached]
	f, err := b.file()
	if err != nil {
		return err
	}
	bs := make([]byte, blk.n*runeBytes)
	for i, r := range b.cache[:blk.n] {
		binary.LittleEndian.PutUint32(bs[i*runeBytes:], uint32(r))
	}
	if _, err := f.WriteAt(bs, blk.start); err != nil {
		return err
	}
	b.dirty = false
	return nil
}

// Get loads the cache with the data from the block at the given index,
// returning a pointer to it.
func (b *Runes) get(i int) (*block, error) {
	if b.cached == i {
		return &b.blocks[i], nil
	}
	if err := b.put(); err != nil {
		return nil, err
	}

	blk := b.blocks[i]
	f, err := b.file()
	if err != nil {
		return nil, err
	}
	bs := make([]byte, blk.n*runeBytes)
	if _, err := f.ReadAt(bs, blk.start); err != nil {
		if err == io.EOF {
			panic("unexpected EOF")
		}
		return nil, err
	}
	j := 0
	for len(bs) > 0 {
		b.cache[j] = rune(binary.LittleEndian.Uint32(bs))
		bs = bs[runeBytes:]
		j++
	}
	b.cached = i
	b.dirty = false
	return &b.blocks[i], nil
}
<commit_msg>Remember the start address of the cached block. This way we don't have to recompute it to read a single rune.<commit_after>// Package buffer provides unbounded, file-backed rune buffers.
package buffer

import (
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"strconv"
)

// RuneBytes is the number of bytes in Go's rune type.
const runeBytes = 4

// A Runes is an unbounded rune buffer backed by a file.
type Runes struct {
	// F is the file that backs the buffer. It is created lazily.
	f *os.File
	// BlockSize is the maximum number of runes in a block.
	blockSize int
	// Blocks contains all blocks of the buffer in order.
	// Free contains blocks that are free to be re-allocated.
	blocks, free []block
	// End is the byte offset of the end of the backing file.
	end int64

	// Cache is the index of the block whose data is currently cached.
	cached int
	// Cached0 is the address of the first rune in the cached block.
	cached0 int64
	// Cache is the cached data.
	cache []rune
	// Dirty tracks whether the cached data has changed since it was read.
	dirty bool

	// Size is the number of runes in the buffer.
	size int64
}

// A block describes a portion of the buffer and its location in the backing file.
type block struct {
	// Start is the byte offset of the block in the file.
	start int64
	// N is the number of runes in the block.
	n int
}

// New returns a new, empty buffer.
// No more than blockSize runes are cached in memory.
func New(blockSize int) *Runes {
	return &Runes{
		blockSize: blockSize,
		cached:    -1,
		cache:     make([]rune, blockSize),
	}
}

// Close closes the buffer and removes it's backing file.
func (b *Runes) Close() error {
	b.cache = nil
	if b.f == nil {
		return nil
	}
	path := b.f.Name()
	if err := b.f.Close(); err != nil {
		return err
	}
	return os.Remove(path)
}

// Size returns the number of runes in the buffer.
func (b *Runes) Size() int64 { return b.size }

// A RuneReadError is paniced if Rune encounters an error
// reading a rune that is in the bounds of the buffer.
type RuneReadError struct{ error }

// RecoverRuneReadError recovers a RuneReadError,
// setting the error pointed to by err to the error.
// If the recovered error is not of type RuneReadError,
// then it is re-paniced.
// This function is intended to be called in a defer statement
// with err as the address of a named error return.
func RecoverRuneReadError(err *error) {
	switch e := recover().(type) {
	case nil:
		return
	case RuneReadError:
		*err = e.error
	default:
		panic(e)
	}
}

// Rune returns the rune at the given offset.
// If the rune is out of range it panics.
// If there is an error reading, it panics a RuneReadError containing the error.
func (b *Runes) Rune(offs int64) rune {
	if offs < 0 || offs > b.Size() {
		panic("rune index out of bounds")
	}
	if q0 := b.cached0; q0 <= offs && offs < q0+int64(b.blocks[b.cached].n) {
		return b.cache[offs-q0]
	}
	i, q0 := b.blockAt(offs)
	if _, err := b.get(i); err != nil {
		panic(RuneReadError{err})
	}
	return b.cache[offs-q0]
}

// Read reads runes from the buffer beginning at a given offset.
// The return value is the number of runes read.
// If fewer than len(rs) runes are read then the error states why.
// If the offset is beyond the end of the buffer, 0 and io.EOF are returned.
func (b *Runes) Read(rs []rune, offs int64) (int, error) {
	switch {
	case offs < 0:
		return 0, errors.New("invalid offset: " + strconv.FormatInt(offs, 10))
	case offs == b.Size() && len(rs) == 0:
		return 0, nil
	case offs >= b.Size():
		return 0, io.EOF
	}
	var tot int
	for len(rs) > 0 {
		if offs == b.Size() {
			return tot, io.EOF
		}
		i, q0 := b.blockAt(offs)
		blk, err := b.get(i)
		if err != nil {
			return tot, err
		}
		o := int(offs - q0)
		m := copy(rs, b.cache[o:blk.n])
		rs = rs[m:]
		offs += int64(m)
		tot += m
	}
	return tot, nil
}

// Insert inserts runes into the buffer at the given offset..
// The return value is the number of runes added and any error that was encountered.
// It is an error to add at a negative offset or an offset beyond the buffer size.
func (b *Runes) Insert(rs []rune, offs int64) (int, error) {
	if offs < 0 || offs > b.Size() {
		return 0, errors.New("invalid offset: " + strconv.FormatInt(offs, 10))
	}
	var tot int
	for len(rs) > 0 {
		i, q0 := b.blockAt(offs)
		blk, err := b.get(i)
		if err != nil {
			return tot, err
		}
		m := b.blockSize - blk.n
		if m == 0 {
			if i, err = b.insertAt(offs); err != nil {
				return tot, err
			}
			if blk, err = b.get(i); err != nil {
				return tot, err
			}
			q0 = offs
			m = b.blockSize
		}
		if m > len(rs) {
			m = len(rs)
		}
		o := int(offs - q0)
		copy(b.cache[o+m:], b.cache[o:blk.n])
		copy(b.cache[o:], rs[:m])
		b.dirty = true
		rs = rs[m:]
		blk.n += m
		b.size += int64(m)
		offs += int64(m)
		tot += m
	}
	return tot, nil
}

// Delete deletes runes from the buffer starting at the given offset.
// The return value is the number of runes deleted.
// If fewer than n runes are deleted, the error states why.
func (b *Runes) Delete(n, offs int64) (int64, error) {
	if n < 0 {
		panic("bad count: " + strconv.FormatInt(n, 10))
	}
	if offs < 0 || offs+n > b.Size() {
		return 0, errors.New("invalid offset: " + strconv.FormatInt(offs, 10))
	}
	var tot int64
	for n > 0 {
		i, q0 := b.blockAt(offs)
		blk, err := b.get(i)
		if err != nil {
			return tot, err
		}
		o := int(offs - q0)
		m := blk.n - o
		if int64(m) > n {
			m = int(n)
		}
		if o == 0 && n >= int64(blk.n) {
			// Remove the entire block.
			b.freeBlock(*blk)
			b.blocks = append(b.blocks[:i], b.blocks[i+1:]...)
			b.cached = -1
		} else {
			// Remove a portion of the block.
			copy(b.cache[o:], b.cache[o+m:])
			b.dirty = true
			blk.n -= m
		}
		n -= int64(m)
		tot += int64(m)
		b.size -= int64(m)
	}
	return tot, nil
}

func (b *Runes) allocBlock() block {
	if l := len(b.free); l > 0 {
		blk := b.free[l-1]
		b.free = b.free[:l-1]
		return blk
	}
	blk := block{start: b.end}
	b.end += int64(b.blockSize * runeBytes)
	return blk
}

func (b *Runes) freeBlock(blk block) {
	b.free = append(b.free, block{start: blk.start})
}

// BlockAt returns the index and start address of the block containing the address.
// If the address is one beyond the end of the file, a new block is allocated.
// BlockAt panics if the address is negative or more than one past the end.
func (b *Runes) blockAt(at int64) (int, int64) {
	if at < 0 || at > b.Size() {
		panic("invalid offset: " + strconv.FormatInt(at, 10))
	}
	if at == b.Size() {
		i := len(b.blocks)
		blk := b.allocBlock()
		b.blocks = append(b.blocks[:i], append([]block{blk}, b.blocks[i:]...)...)
		return i, at
	}
	var q0 int64
	for i, blk := range b.blocks {
		if q0 <= at && at < q0+int64(blk.n) {
			return i, q0
		}
		q0 += int64(blk.n)
	}
	panic("impossible")
}

// insertAt inserts a block at the address and returns the new block's index.
// If a block contains the address then it is split.
func (b *Runes) insertAt(at int64) (int, error) {
	i, q0 := b.blockAt(at)
	o := int(at - q0)
	blk := b.blocks[i]
	if at == q0 {
		// Adding immediately before blk, no need to split.
		nblk := b.allocBlock()
		b.blocks = append(b.blocks[:i], append([]block{nblk}, b.blocks[i:]...)...)
		if b.cached == i {
			b.cached = i + 1
		}
		return i, nil
	}

	// Splitting blk.
	// Make sure it's both on disk and in the cache.
	if b.cached == i && b.dirty {
		if err := b.put(); err != nil {
			return -1, err
		}
	} else if _, err := b.get(i); err != nil {
		return -1, err
	}

	// Resize blk.
	b.blocks[i].n = int(o)

	// Insert the new, empty block.
	nblk := b.allocBlock()
	b.blocks = append(b.blocks[:i+1], append([]block{nblk}, b.blocks[i+1:]...)...)

	// Allocate a block for the second half of blk and set it as the cache.
	// The next put will write it out.
	nblk = b.allocBlock()
	b.blocks = append(b.blocks[:i+2], append([]block{nblk}, b.blocks[i+2:]...)...)
	b.blocks[i+2].n = blk.n - o
	copy(b.cache, b.cache[o:])
	b.cached = i + 2
	b.dirty = true

	return i + 1, nil
}

// File returns an *os.File, creating a new file if one is not created yet.
func (b *Runes) file() (*os.File, error) {
	if b.f == nil {
		f, err := ioutil.TempFile(os.TempDir(), "edit")
		if err != nil {
			return nil, err
		}
		b.f = f
	}
	return b.f, nil
}

// Put writes the cached block back to the file.
func (b *Runes) put() error {
	if b.cached < 0 || !b.dirty || len(b.cache) == 0 {
		return nil
	}
	blk := b.blocks[b.cached]
	f, err := b.file()
	if err != nil {
		return err
	}
	bs := make([]byte, blk.n*runeBytes)
	for i, r := range b.cache[:blk.n] {
		binary.LittleEndian.PutUint32(bs[i*runeBytes:], uint32(r))
	}
	if _, err := f.WriteAt(bs, blk.start); err != nil {
		return err
	}
	b.dirty = false
	return nil
}

// Get loads the cache with the data from the block at the given index,
// returning a pointer to it.
func (b *Runes) get(i int) (*block, error) {
	if b.cached == i {
		return &b.blocks[i], nil
	}
	if err := b.put(); err != nil {
		return nil, err
	}

	blk := b.blocks[i]
	f, err := b.file()
	if err != nil {
		return nil, err
	}
	bs := make([]byte, blk.n*runeBytes)
	if _, err := f.ReadAt(bs, blk.start); err != nil {
		if err == io.EOF {
			panic("unexpected EOF")
		}
		return nil, err
	}
	j := 0
	for len(bs) > 0 {
		b.cache[j] = rune(binary.LittleEndian.Uint32(bs))
		bs = bs[runeBytes:]
		j++
	}
	b.cached = i
	b.dirty = false
	b.cached0 = 0
	for j := 0; j < i; j++ {
		b.cached0 += int64(b.blocks[j].n)
	}
	return &b.blocks[i], nil
}
<|endoftext|>


<commit_before>package zeroconf

import (
	"fmt"
	"log"
	"net"

	dns "github.com/miekg/godns"
)

func Listen(zone *Zone) *listener {
        listener := &listener{
                socket: openIPv4Socket(net.IPv4zero),
                zone:   zone,
        }

        if err := listener.socket.JoinGroup(nil, net.IPv4(224, 0, 0, 251)); err != nil {
                log.Fatal(err)
        }

        go listener.mainloop()
	return listener
}

func openIPv4Socket(ip net.IP) *net.UDPConn {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{
		IP:   ip,
		Port: 5353,
	})
	if err != nil {
		log.Fatal(err)
	}
	return conn
}

func mcastInterfaces() []net.Interface {
	ifaces := make([]net.Interface, 0)
	interfaces, err := net.Interfaces()
	if err != nil {
		log.Fatal(err)
	}
	for _, i := range interfaces {
		if isMulticast(i) {
			fmt.Printf("%#v\n", i)
			ifaces = append(ifaces, i)
		}
	}
	return ifaces
}

func isMulticast(i net.Interface) bool {
	return (i.Flags&net.FlagUp > 0) && (i.Flags&net.FlagMulticast > 0)
}

type listener struct {
	socket *net.UDPConn
	zone   *Zone
}

func (l *listener) mainloop() {
	buf := make([]byte, 1500)
	for {
		read, _, err := l.socket.ReadFromUDP(buf)
		if err != nil {
			log.Fatal(err)
		}
		msg := new(dns.Msg)
		msg.Unpack(buf[:read])
		if isQuestion(msg) {
			var answers []dns.RR
			for _, question := range msg.Question {
				for result := range l.zone.Query(question) {
					if result.publish {
						answers = append(answers, result.rr)
					}
				}
			}
			l.SendResponse(answers)
		} else {
			for _, rr := range msg.Answer {
				l.zone.Add(&Entry{
					expires: 2 ^ 31,
					publish: false,
					rr:      rr,
				})
			}
		}
	}
}

func (l *listener) SendResponse(answers []dns.RR) {
	response := &dns.Msg {
		MsgHdr: dns.MsgHdr{
			Response: true,
		},
		Answer: answers,
	}
  	if buf, ok := response.Pack() ; ok {
       		l.socket.Write(buf)
        }
}

func isQuestion(msg *dns.Msg) bool {
	return !msg.MsgHdr.Response
}
<commit_msg>Switch to msg.IsQuestion<commit_after>package zeroconf

import (
	"fmt"
	"log"
	"net"

	dns "github.com/miekg/godns"
)

func Listen(zone *Zone) *listener {
        listener := &listener{
                socket: openIPv4Socket(net.IPv4zero),
                zone:   zone,
        }

        if err := listener.socket.JoinGroup(nil, net.IPv4(224, 0, 0, 251)); err != nil {
                log.Fatal(err)
        }

        go listener.mainloop()
	return listener
}

func openIPv4Socket(ip net.IP) *net.UDPConn {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{
		IP:   ip,
		Port: 5353,
	})
	if err != nil {
		log.Fatal(err)
	}
	return conn
}

func mcastInterfaces() []net.Interface {
	ifaces := make([]net.Interface, 0)
	interfaces, err := net.Interfaces()
	if err != nil {
		log.Fatal(err)
	}
	for _, i := range interfaces {
		if isMulticast(i) {
			fmt.Printf("%#v\n", i)
			ifaces = append(ifaces, i)
		}
	}
	return ifaces
}

func isMulticast(i net.Interface) bool {
	return (i.Flags&net.FlagUp > 0) && (i.Flags&net.FlagMulticast > 0)
}

type listener struct {
	socket *net.UDPConn
	zone   *Zone
}

func (l *listener) mainloop() {
	buf := make([]byte, 1500)
	for {
		read, _, err := l.socket.ReadFromUDP(buf)
		if err != nil {
			log.Fatal(err)
		}
		msg := new(dns.Msg)
		msg.Unpack(buf[:read])
		if msg.IsQuestion() {
			var answers []dns.RR
			for _, question := range msg.Question {
				for result := range l.zone.Query(question) {
					if result.publish {
						answers = append(answers, result.rr)
					}
				}
			}
			l.SendResponse(answers)
		} else {
			for _, rr := range msg.Answer {
				l.zone.Add(&Entry{
					expires: 2 ^ 31,
					publish: false,
					rr:      rr,
				})
			}
		}
	}
}

func (l *listener) SendResponse(answers []dns.RR) {
	response := &dns.Msg {
		MsgHdr: dns.MsgHdr{
			Response: true,
		},
		Answer: answers,
	}
  	if buf, ok := response.Pack() ; ok {
       		l.socket.Write(buf)
        }
}

<|endoftext|>


<commit_before>// Copyright (c) 2016 Pani Networks
// 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 kubernetes listens to Kubernetes for policy updates.
package kubernetes

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/romana/core/common"
	"github.com/romana/core/tenant"
	"io"
	"log"
	"net/http"
	"strings"
)

// readChunk reads the next chunk from the provided reader.
func readChunk(reader io.Reader) ([]byte, error) {
	var result []byte
	for {
		buf := make([]byte, bytes.MinRead)
		n, err := reader.Read(buf)
		if n < bytes.MinRead {
			result = append(result, buf[0:n]...)
			if err != nil {
				return result, err
			}
			break
		}
		result = append(result, buf...)
	}
	return result, nil
}

type networkPolicyAction int

const (
	networkPolicyActionDelete networkPolicyAction = iota
	networkPolicyActionAdd
	networkPolicyActionModify
)

// kubeListener is a Service that listens to updates
// from Kubernetes by connecting to the endpoints specified
// and consuming chunked JSON documents. The endpoints are
// constructed from kubeURL and the following paths:
// 1. namespaceNotificationPath for namespace additions/deletions
// 2. policyNotificationPathPrefix + <namespace name> + policyNotificationPathPostfix
//    for policy additions/deletions.
type kubeListener struct {
	config                        common.ServiceConfig
	restClient                    *common.RestClient
	kubeURL                       string
	namespaceNotificationPath     string
	policyNotificationPathPrefix  string
	policyNotificationPathPostfix string
	segmentLabelName              string
}

// Routes returns various routes used in the service.
func (l *kubeListener) Routes() common.Routes {
	routes := common.Routes{}
	return routes
}

// Name implements method of Service interface.
func (l *kubeListener) Name() string {
	return "kubernetesListener"
}

// SetConfig implements SetConfig function of the Service interface.
func (l *kubeListener) SetConfig(config common.ServiceConfig) error {
	m := config.ServiceSpecific
	if m["kubernetes_url"] == "" {
		return errors.New("kubernetes_url required")
	}
	l.kubeURL = m["kubernetes_url"].(string)

	if m["namespace_notification_path"] == "" {
		return errors.New("namespace_notification_path required")
	}
	l.namespaceNotificationPath = m["namespace_notification_path"].(string)

	if m["policy_notification_path_prefix"] == "" {
		return errors.New("policy_notification_path_prefix required")
	}
	l.policyNotificationPathPrefix = m["policy_notification_path_prefix"].(string)

	if m["policy_notification_path_postfix"] == "" {
		return errors.New("policy_notification_path_postfix required")
	}
	l.policyNotificationPathPostfix = m["policy_notification_path_postfix"].(string)

	if m["segment_label_name"] == "" {
		return errors.New("segment_label_name required")
	}
	l.segmentLabelName = m["segment_label_name"].(string)

	return nil
}

// Run configures and runs listener service.
func Run(rootServiceURL string, cred *common.Credential) (*common.RestServiceInfo, error) {
	clientConfig := common.GetDefaultRestClientConfig(rootServiceURL)
	clientConfig.Credential = cred
	client, err := common.NewRestClient(clientConfig)
	if err != nil {
		return nil, err
	}
	kubeListener := &kubeListener{}
	kubeListener.restClient = client
	config, err := client.GetServiceConfig(kubeListener.Name())
	if err != nil {
		return nil, err
	}
	return common.InitializeService(kubeListener, *config)
}

// getOrAddSegment finds a segment (based on segment selector).
// If not found, it adds one.
func (l *kubeListener) getOrAddSegment(namespace string, kubeSegmentName string) (*tenant.Segment, error) {
	ten := &tenant.Tenant{}
	ten.Name = namespace
	// TODO this should be changed to find EXACTLY one after deletion functionality is implemented
	err := l.restClient.Find(ten, common.FindLast)
	if err != nil {
		return nil, err
	}

	seg := &tenant.Segment{}
	seg.Name = kubeSegmentName
	seg.TenantID = ten.ID
	err = l.restClient.Find(seg, common.FindExactlyOne)
	if err == nil {
		return seg, nil
	}

	switch err := err.(type) {
	case common.HttpError:
		if err.StatusCode == http.StatusNotFound {
			// Not found, so let's create a segment.
			segreq := tenant.Segment{Name: kubeSegmentName, TenantID: ten.ID}
			segURL, err2 := l.restClient.GetServiceUrl("tenant")
			if err2 != nil {
				return nil, err2
			}
			segURL = fmt.Sprintf("%s/tenants/%d/segments", segURL, ten.ID)
			err2 = l.restClient.Post(segURL, segreq, seg)
			if err2 == nil {
				// Successful creation.
				return seg, nil
			}
			// Creation of non-existing segment gave an error.
			switch err2 := err2.(type) {
			case common.HttpError:
				// Maybe someone else just created a segment between the original
				// lookup and now?
				if err2.StatusCode == http.StatusConflict {
					switch details := err2.Details.(type) {
					case tenant.Segment:
						// We expect the existing segment to be returned in the details field.
						return &details, nil
					default:
						// This is unexpected...
						return nil, err
					}
				}
				// Any other HTTP error other than a Conflict here - return it.
				return nil, err2
			default:
				// Any other error - return it
				return nil, err2
			}
		}
		// Any other HTTP error other than a Not found here - return it
		return nil, err
	default:
		// Any other error - return it
		return nil, err
	}
}

// resolveTenantByName retrieves tenant information from romana.
func (l *kubeListener) resolveTenantByName(tenantName string) (*tenant.Tenant, error) {
	t := &tenant.Tenant{}
	err := l.restClient.Find(t, common.FindLast)
	if err != nil {
		return t, err
	}
	return t, nil
}

// translateNetworkPolicy translates a Kubernetes policy into
// Romana policy (see common.Policy) with the following rules:
// 1. Kubernetes Namespace corresponds to Romana Tenant
// 2. If Romana Tenant does not exist it is an error (a tenant should
//    automatically have been created when the namespace was added)
func (l *kubeListener) translateNetworkPolicy(kubePolicy *KubeObject) (common.Policy, error) {
	policyName := kubePolicy.Metadata.Name
	romanaPolicy := &common.Policy{Direction: common.PolicyDirectionIngress, Name: policyName, ExternalID: policyName}
	ns := kubePolicy.Metadata.Namespace
	// TODO actually look up tenant K8S ID.
	t, err := l.resolveTenantByName(ns)
	log.Printf("translateNetworkPolicy(): For namespace %s got %+v / %+v", ns, t, err)
	if err != nil {
		return *romanaPolicy, err
	}
	tenantID := t.ID
	tenantExternalID := t.ExternalID

	kubeSegmentID := kubePolicy.Spec.PodSelector.MatchLabels[l.segmentLabelName]
	if kubeSegmentID == "" {
		return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
	}

	segment, err := l.getOrAddSegment(ns, kubeSegmentID)
	if err != nil {
		return *romanaPolicy, err
	}
	segmentID := segment.ID
	appliedTo := common.Endpoint{TenantID: tenantID, SegmentID: segmentID}
	romanaPolicy.AppliedTo = make([]common.Endpoint, 1)
	romanaPolicy.AppliedTo[0] = appliedTo
	romanaPolicy.Peers = []common.Endpoint{}
	romanaPolicy.Rules = common.Rules{}
	// TODO range
	// from := kubePolicy.Spec.Ingress[0].From
	// This is subject to change once the network specification in Kubernetes is finalized.
	// Right now it is a work in progress.
	for _, ingress := range kubePolicy.Spec.Ingress {
		for _, entry := range ingress.From {
			pods := entry.Pods
			fromKubeSegmentID := pods.MatchLabels[l.segmentLabelName]
			if fromKubeSegmentID == "" {
				return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
			}
			fromSegment, err := l.getOrAddSegment(ns, fromKubeSegmentID)
			if err != nil {
				return *romanaPolicy, err
			}
			peer := common.Endpoint{TenantID: tenantID, TenantExternalID: tenantExternalID, SegmentID: fromSegment.ID, SegmentExternalID: fromSegment.ExternalID}
			romanaPolicy.Peers = append(romanaPolicy.Peers, peer)
		}
		// TODO range
		// toPorts := kubePolicy.Spec.Ingress[0].ToPorts
		for _, toPort := range ingress.ToPorts {
			proto := strings.ToLower(toPort.Protocol)
			ports := []uint{toPort.Port}
			rule := common.Rule{Protocol: proto, Ports: ports}
			romanaPolicy.Rules = append(romanaPolicy.Rules, rule)
		}
	}
	err = romanaPolicy.Validate()
	if err != nil {
		return *romanaPolicy, err
	}
	return *romanaPolicy, nil
}

func (l *kubeListener) applyNetworkPolicy(action networkPolicyAction, romanaNetworkPolicy common.Policy) error {
	policyURL, err := l.restClient.GetServiceUrl("policy")
	if err != nil {
		return err
	}
	policyURL = fmt.Sprintf("%s/policies", policyURL)
	policyStr, _ := json.Marshal(romanaNetworkPolicy)
	switch action {
	case networkPolicyActionAdd:
		log.Printf("Applying policy %s", policyStr)
		err := l.restClient.Post(policyURL, romanaNetworkPolicy, &romanaNetworkPolicy)
		if err != nil {
			return err
		}
	case networkPolicyActionDelete:
		log.Printf("Deleting policy policy %s", policyStr)
		err := l.restClient.Delete(policyURL, romanaNetworkPolicy, &romanaNetworkPolicy)
		if err != nil {
			return err
		}
	default:
		return errors.New("Unsupported operation")
	}
	return nil
}

func (l *kubeListener) Initialize() error {
	log.Printf("%s: Starting server", l.Name())
	nsURL, err := common.CleanURL(fmt.Sprintf("%s%s", l.kubeURL, l.namespaceNotificationPath))
	if err != nil {
		return err
	}
	log.Printf("Starting to listen on %s", nsURL)
	done := make(chan Done)
	nsEvents, err := l.nsWatch(done, nsURL)
	if err != nil {
		log.Fatal("Namespace watcher failed to start", err)
	}

	events := l.conductor(nsEvents, done)
	l.process(events, done)
	log.Println("All routines started")
	return nil
}

// CreateSchema is placeholder for now.
func CreateSchema(rootServiceURL string, overwrite bool) error {
	return nil
}
<commit_msg>Provide tenantName when resolving.<commit_after>// Copyright (c) 2016 Pani Networks
// 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 kubernetes listens to Kubernetes for policy updates.
package kubernetes

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/romana/core/common"
	"github.com/romana/core/tenant"
	"io"
	"log"
	"net/http"
	"strings"
)

// readChunk reads the next chunk from the provided reader.
func readChunk(reader io.Reader) ([]byte, error) {
	var result []byte
	for {
		buf := make([]byte, bytes.MinRead)
		n, err := reader.Read(buf)
		if n < bytes.MinRead {
			result = append(result, buf[0:n]...)
			if err != nil {
				return result, err
			}
			break
		}
		result = append(result, buf...)
	}
	return result, nil
}

type networkPolicyAction int

const (
	networkPolicyActionDelete networkPolicyAction = iota
	networkPolicyActionAdd
	networkPolicyActionModify
)

// kubeListener is a Service that listens to updates
// from Kubernetes by connecting to the endpoints specified
// and consuming chunked JSON documents. The endpoints are
// constructed from kubeURL and the following paths:
// 1. namespaceNotificationPath for namespace additions/deletions
// 2. policyNotificationPathPrefix + <namespace name> + policyNotificationPathPostfix
//    for policy additions/deletions.
type kubeListener struct {
	config                        common.ServiceConfig
	restClient                    *common.RestClient
	kubeURL                       string
	namespaceNotificationPath     string
	policyNotificationPathPrefix  string
	policyNotificationPathPostfix string
	segmentLabelName              string
}

// Routes returns various routes used in the service.
func (l *kubeListener) Routes() common.Routes {
	routes := common.Routes{}
	return routes
}

// Name implements method of Service interface.
func (l *kubeListener) Name() string {
	return "kubernetesListener"
}

// SetConfig implements SetConfig function of the Service interface.
func (l *kubeListener) SetConfig(config common.ServiceConfig) error {
	m := config.ServiceSpecific
	if m["kubernetes_url"] == "" {
		return errors.New("kubernetes_url required")
	}
	l.kubeURL = m["kubernetes_url"].(string)

	if m["namespace_notification_path"] == "" {
		return errors.New("namespace_notification_path required")
	}
	l.namespaceNotificationPath = m["namespace_notification_path"].(string)

	if m["policy_notification_path_prefix"] == "" {
		return errors.New("policy_notification_path_prefix required")
	}
	l.policyNotificationPathPrefix = m["policy_notification_path_prefix"].(string)

	if m["policy_notification_path_postfix"] == "" {
		return errors.New("policy_notification_path_postfix required")
	}
	l.policyNotificationPathPostfix = m["policy_notification_path_postfix"].(string)

	if m["segment_label_name"] == "" {
		return errors.New("segment_label_name required")
	}
	l.segmentLabelName = m["segment_label_name"].(string)

	return nil
}

// Run configures and runs listener service.
func Run(rootServiceURL string, cred *common.Credential) (*common.RestServiceInfo, error) {
	clientConfig := common.GetDefaultRestClientConfig(rootServiceURL)
	clientConfig.Credential = cred
	client, err := common.NewRestClient(clientConfig)
	if err != nil {
		return nil, err
	}
	kubeListener := &kubeListener{}
	kubeListener.restClient = client
	config, err := client.GetServiceConfig(kubeListener.Name())
	if err != nil {
		return nil, err
	}
	return common.InitializeService(kubeListener, *config)
}

// getOrAddSegment finds a segment (based on segment selector).
// If not found, it adds one.
func (l *kubeListener) getOrAddSegment(namespace string, kubeSegmentName string) (*tenant.Segment, error) {
	ten := &tenant.Tenant{}
	ten.Name = namespace
	// TODO this should be changed to find EXACTLY one after deletion functionality is implemented
	err := l.restClient.Find(ten, common.FindLast)
	if err != nil {
		return nil, err
	}

	seg := &tenant.Segment{}
	seg.Name = kubeSegmentName
	seg.TenantID = ten.ID
	err = l.restClient.Find(seg, common.FindExactlyOne)
	if err == nil {
		return seg, nil
	}

	switch err := err.(type) {
	case common.HttpError:
		if err.StatusCode == http.StatusNotFound {
			// Not found, so let's create a segment.
			segreq := tenant.Segment{Name: kubeSegmentName, TenantID: ten.ID}
			segURL, err2 := l.restClient.GetServiceUrl("tenant")
			if err2 != nil {
				return nil, err2
			}
			segURL = fmt.Sprintf("%s/tenants/%d/segments", segURL, ten.ID)
			err2 = l.restClient.Post(segURL, segreq, seg)
			if err2 == nil {
				// Successful creation.
				return seg, nil
			}
			// Creation of non-existing segment gave an error.
			switch err2 := err2.(type) {
			case common.HttpError:
				// Maybe someone else just created a segment between the original
				// lookup and now?
				if err2.StatusCode == http.StatusConflict {
					switch details := err2.Details.(type) {
					case tenant.Segment:
						// We expect the existing segment to be returned in the details field.
						return &details, nil
					default:
						// This is unexpected...
						return nil, err
					}
				}
				// Any other HTTP error other than a Conflict here - return it.
				return nil, err2
			default:
				// Any other error - return it
				return nil, err2
			}
		}
		// Any other HTTP error other than a Not found here - return it
		return nil, err
	default:
		// Any other error - return it
		return nil, err
	}
}

// resolveTenantByName retrieves tenant information from romana.
func (l *kubeListener) resolveTenantByName(tenantName string) (*tenant.Tenant, error) {
	t := &tenant.Tenant{Name: tenantName}
	err := l.restClient.Find(t, common.FindLast)
	if err != nil {
		return t, err
	}
	return t, nil
}

// translateNetworkPolicy translates a Kubernetes policy into
// Romana policy (see common.Policy) with the following rules:
// 1. Kubernetes Namespace corresponds to Romana Tenant
// 2. If Romana Tenant does not exist it is an error (a tenant should
//    automatically have been created when the namespace was added)
func (l *kubeListener) translateNetworkPolicy(kubePolicy *KubeObject) (common.Policy, error) {
	policyName := kubePolicy.Metadata.Name
	romanaPolicy := &common.Policy{Direction: common.PolicyDirectionIngress, Name: policyName, ExternalID: policyName}
	ns := kubePolicy.Metadata.Namespace
	// TODO actually look up tenant K8S ID.
	t, err := l.resolveTenantByName(ns)
	log.Printf("translateNetworkPolicy(): For namespace %s got %+v / %+v", ns, t, err)
	if err != nil {
		return *romanaPolicy, err
	}
	tenantID := t.ID
	tenantExternalID := t.ExternalID

	kubeSegmentID := kubePolicy.Spec.PodSelector.MatchLabels[l.segmentLabelName]
	if kubeSegmentID == "" {
		return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
	}

	segment, err := l.getOrAddSegment(ns, kubeSegmentID)
	if err != nil {
		return *romanaPolicy, err
	}
	segmentID := segment.ID
	appliedTo := common.Endpoint{TenantID: tenantID, SegmentID: segmentID}
	romanaPolicy.AppliedTo = make([]common.Endpoint, 1)
	romanaPolicy.AppliedTo[0] = appliedTo
	romanaPolicy.Peers = []common.Endpoint{}
	romanaPolicy.Rules = common.Rules{}
	// TODO range
	// from := kubePolicy.Spec.Ingress[0].From
	// This is subject to change once the network specification in Kubernetes is finalized.
	// Right now it is a work in progress.
	for _, ingress := range kubePolicy.Spec.Ingress {
		for _, entry := range ingress.From {
			pods := entry.Pods
			fromKubeSegmentID := pods.MatchLabels[l.segmentLabelName]
			if fromKubeSegmentID == "" {
				return *romanaPolicy, common.NewError("Expected segment to be specified in podSelector part as '%s'", l.segmentLabelName)
			}
			fromSegment, err := l.getOrAddSegment(ns, fromKubeSegmentID)
			if err != nil {
				return *romanaPolicy, err
			}
			peer := common.Endpoint{TenantID: tenantID, TenantExternalID: tenantExternalID, SegmentID: fromSegment.ID, SegmentExternalID: fromSegment.ExternalID}
			romanaPolicy.Peers = append(romanaPolicy.Peers, peer)
		}
		// TODO range
		// toPorts := kubePolicy.Spec.Ingress[0].ToPorts
		for _, toPort := range ingress.ToPorts {
			proto := strings.ToLower(toPort.Protocol)
			ports := []uint{toPort.Port}
			rule := common.Rule{Protocol: proto, Ports: ports}
			romanaPolicy.Rules = append(romanaPolicy.Rules, rule)
		}
	}
	err = romanaPolicy.Validate()
	if err != nil {
		return *romanaPolicy, err
	}
	return *romanaPolicy, nil
}

func (l *kubeListener) applyNetworkPolicy(action networkPolicyAction, romanaNetworkPolicy common.Policy) error {
	policyURL, err := l.restClient.GetServiceUrl("policy")
	if err != nil {
		return err
	}
	policyURL = fmt.Sprintf("%s/policies", policyURL)
	policyStr, _ := json.Marshal(romanaNetworkPolicy)
	switch action {
	case networkPolicyActionAdd:
		log.Printf("Applying policy %s", policyStr)
		err := l.restClient.Post(policyURL, romanaNetworkPolicy, &romanaNetworkPolicy)
		if err != nil {
			return err
		}
	case networkPolicyActionDelete:
		log.Printf("Deleting policy policy %s", policyStr)
		err := l.restClient.Delete(policyURL, romanaNetworkPolicy, &romanaNetworkPolicy)
		if err != nil {
			return err
		}
	default:
		return errors.New("Unsupported operation")
	}
	return nil
}

func (l *kubeListener) Initialize() error {
	log.Printf("%s: Starting server", l.Name())
	nsURL, err := common.CleanURL(fmt.Sprintf("%s%s", l.kubeURL, l.namespaceNotificationPath))
	if err != nil {
		return err
	}
	log.Printf("Starting to listen on %s", nsURL)
	done := make(chan Done)
	nsEvents, err := l.nsWatch(done, nsURL)
	if err != nil {
		log.Fatal("Namespace watcher failed to start", err)
	}

	events := l.conductor(nsEvents, done)
	l.process(events, done)
	log.Println("All routines started")
	return nil
}

// CreateSchema is placeholder for now.
func CreateSchema(rootServiceURL string, overwrite bool) error {
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 transformers

import (
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// defaultNameReferencePathConfigs is the default configuration for updating
// the fields reference the name of other resources.
var defaultNameReferencePathConfigs = []referencePathConfig{
	{
		referencedGVK: schema.GroupVersionKind{
			Version: "v1",
			Kind:    "ConfigMap",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
		},
	},
	{
		referencedGVK: schema.GroupVersionKind{
			Version: "v1",
			Kind:    "Secret",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
		},
	},
}
<commit_msg>updated namereference config for missing references.<commit_after>/*
Copyright 2018 The Kubernetes 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 transformers

import (
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// defaultNameReferencePathConfigs is the default configuration for updating
// the fields reference the name of other resources.
var defaultNameReferencePathConfigs = []referencePathConfig{
	{
		referencedGVK: schema.GroupVersionKind{
			Version: "v1",
			Kind:    "ConfigMap",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "volumes", "configMap", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "env", "valueFrom", "configMapKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "envFrom", "configMapRef", "name"},
				CreateIfNotPresent: false,
			},
		},
	},
	{
		referencedGVK: schema.GroupVersionKind{
			Version: "v1",
			Kind:    "Secret",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Version: "v1",
					Kind:    "Pod",
				},
				Path:               []string{"spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicaSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "volumes", "secret", "secretName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "env", "valueFrom", "secretKeyRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "containers", "envFrom", "secretRef", "name"},
				CreateIfNotPresent: false,
			},
		},
	},
	{
		// StatefulSet references headless service, so need to update the references.
		referencedGVK: schema.GroupVersionKind{
			Version: "v1",
			Kind:    "Service",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Group: "apps",
					Kind:  "StatefulSet",
				},
				Path:               []string{"spec", "serviceName"},
				CreateIfNotPresent: false,
			},
		},
	},
	{
		referencedGVK: schema.GroupVersionKind{
			Group: "rbac.authorization.k8s.io",
			Kind:  "Role",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Group: "rbac.authorization.k8s.io",
					Kind:  "RoleBinding",
				},
				Path:               []string{"roleRef", "name"},
				CreateIfNotPresent: false,
			},
		},
	},
	{
		referencedGVK: schema.GroupVersionKind{
			Group: "rbac.authorization.k8s.io",
			Kind:  "ClusterRole",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Group: "rbac.authorization.k8s.io",
					Kind:  "RoleBinding",
				},
				Path:               []string{"roleRef", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Group: "rbac.authorization.k8s.io",
					Kind:  "ClusterRoleBinding",
				},
				Path:               []string{"roleRef", "name"},
				CreateIfNotPresent: false,
			},
		},
	},
	{
		referencedGVK: schema.GroupVersionKind{
			Version: "v1",
			Kind:    "ServiceAccount",
		},
		pathConfigs: []PathConfig{
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Group: "rbac.authorization.k8s.io",
					Kind:  "RoleBinding",
				},
				Path:               []string{"subjects", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Group: "rbac.authorization.k8s.io",
					Kind:  "ClusterRoleBinding",
				},
				Path:               []string{"subjects", "name"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Pod",
				},
				Path:               []string{"spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "StatefulSet",
				},
				Path:               []string{"spec", "template", "spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Deployment",
				},
				Path:               []string{"spec", "template", "spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "ReplicationController",
				},
				Path:               []string{"spec", "template", "spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "CronJob",
				},
				Path:               []string{"spec", "jobTemplate", "spec", "template", "spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "Job",
				},
				Path:               []string{"spec", "template", "spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
			{
				GroupVersionKind: &schema.GroupVersionKind{
					Kind: "DaemonSet",
				},
				Path:               []string{"spec", "template", "spec", "serviceAccountName"},
				CreateIfNotPresent: false,
			},
		},
	},
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"flag"
	"os"
	"runtime"
	"strings"
	"os/exec"
)

const (
	Binary = "easyproxy"
	PKG = "./src"
)

func main() {
	log.Println("start building...")
	flag.Parse()
	if flag.NArg() == 0 {
		log.Println("please use go run build.go to build")
		return
	}
	for _, cmd := range flag.Args() {
		switch cmd {
		case "build":
			clean()
			build()
		case "clean":
			clean()
		default:
			log.Println("unknown command %s", cmd)
		}
	}
}

func execFile() string {
	return "./bin/" + execName()
}

func execName() string {
	name := Binary
	if runtime.GOOS == "windows" {
		name += ".exe"
	}
	return name
}

func build() {
	args := []string{"build", "-ldflags", "-w -s"}
	args = append(args, "-o", execFile())
	args = append(args, PKG)
	runCommand("go", args...)
}

func runCommand(cmd string, args ...string) {
	log.Println(cmd, strings.Join(args, " "))
	command := exec.Command(cmd, args...)
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr
	err := command.Run()
	if err != nil {
		log.Println(err)
	}
}

func clean() {
	runCommand("rm", "-f", execFile())
}
<commit_msg>support windows linx mac building project<commit_after>package main

import (
	"log"
	"flag"
	"os"
	"runtime"
	"strings"
	"os/exec"
	"io"
)

const (
	BinaryDir = "./bin/"
	Binary = "easyproxy"
	ConfDir = "./conf/"
	Config = "default.json"
	PKG = "./src"
)

func main() {
	flag.Parse()
	if flag.NArg() == 0 {
		log.Println("please use go run build.go to build")
		return
	}
	for _, cmd := range flag.Args() {
		switch cmd {
		case "build":
			clean()
			build()
		case "clean":
			clean()
		default:
			log.Println("unknown command %s", cmd)
		}
	}
}

func execFile() string {
	return BinaryDir + execName()
}

func confFile() string {
	return ConfDir + Config
}

func execName() string {
	name := Binary
	if runtime.GOOS == "windows" {
		name += ".exe"
	}
	return name
}

func build() {
	log.Println("start building...")
	args := []string{"build", "-ldflags", "-w -s"}
	args = append(args, "-o", execFile())
	args = append(args, PKG)
	runCommand("go", args...)
	copyConf()
}

func copyConf() {
	DstDir := BinaryDir + ConfDir
	os.Mkdir(DstDir, os.ModePerm)
	src, err := os.Open(confFile())
	if err != nil {
		log.Println("cannot open file:", err)
		return
	}
	defer src.Close()
	dst, err := os.Create(DstDir + Config)
	if err != nil {
		log.Println("create conf file failed:", err)
		return
	}
	defer dst.Close()
	_, err = io.Copy(dst, src)
	if err != nil {
		log.Println("copy conf failed:", err)
	}
}

func runCommand(cmd string, args ...string) {
	log.Println(cmd, strings.Join(args, " "))
	command := exec.Command(cmd, args...)
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr
	err := command.Run()
	if err != nil {
		log.Println(err)
	}
}

func clean() {
	log.Println("start cleaning...")
	err := os.RemoveAll(BinaryDir)
	if err != nil {
		log.Println("clean files failed:", err)
	}
}
<|endoftext|>


<commit_before>package main

const build = "6ebb93970dd2483e230363a05345901099bb8499"
<commit_msg>update build sequence<commit_after>package main

const build = "19ba4232ca25c603a01e39f2436783141e73032f"
<|endoftext|>


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

package main

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestWatchers(t *testing.T) {
	// Setup
	tmpDir, err := ioutil.TempDir("", "ghostunnel-test")
	panicOnError(err)

	tmpFile, err := ioutil.TempFile(tmpDir, "")
	tmpFile.WriteString("test")
	tmpFile.Sync()
	defer os.Remove(tmpFile.Name())

	// Start watching
	watcher := make(chan bool, 1)

	go watchFiles([]string{tmpFile.Name()}, time.Duration(100)*time.Millisecond, watcher)

	time.Sleep(time.Duration(1) * time.Second)

	// Must detect new writes
	tmpFile.WriteString("new")
	tmpFile.Sync()
	tmpFile.Close()

	select {
	case _ = <-watcher:
	case _ = <-time.Tick(time.Duration(1) * time.Second):
		t.Fatalf("timeout, no notification on changed file")
	}

	// Must detect file being replaced
	os.Remove(tmpFile.Name())
	tmpFile, err = os.Create(tmpFile.Name())
	panicOnError(err)

	tmpFile.WriteString("blubb")
	tmpFile.Sync()
	tmpFile.Close()

	select {
	case _ = <-watcher:
	case _ = <-time.Tick(time.Duration(1) * time.Second):
		t.Fatalf("timeout, no notification on changed file")
	}
}
<commit_msg>Add extra unit tests for watcher<commit_after>/*-
 * Copyright 2015 Square Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestWatchers(t *testing.T) {
	// Setup
	tmpDir, err := ioutil.TempDir("", "ghostunnel-test")
	panicOnError(err)

	tmpFile, err := ioutil.TempFile(tmpDir, "")
	tmpFile.WriteString("test")
	tmpFile.Sync()
	defer os.Remove(tmpFile.Name())

	// Start watching
	watcher := make(chan bool, 1)

	go watchFiles([]string{tmpFile.Name()}, time.Duration(100)*time.Millisecond, watcher)

	time.Sleep(time.Duration(1) * time.Second)

	// Must detect new writes
	tmpFile.WriteString("new")
	tmpFile.Sync()
	tmpFile.Close()

	select {
	case _ = <-watcher:
	case _ = <-time.Tick(time.Duration(1) * time.Second):
		t.Fatalf("timeout, no notification on changed file")
	}

	// Must detect file being replaced
	os.Remove(tmpFile.Name())
	tmpFile, err = os.Create(tmpFile.Name())
	panicOnError(err)

	tmpFile.WriteString("blubb")
	tmpFile.Sync()
	tmpFile.Close()

	select {
	case _ = <-watcher:
	case _ = <-time.Tick(time.Duration(1) * time.Second):
		t.Fatalf("timeout, no notification on changed file")
	}
}

func TestHashFilesNonExistent(t *testing.T) {
	res := hashFiles([]string{"./does-not-exist"})
	if len(res) > 0 {
		t.Error("hash files generated hash for non-existent file")
	}
}

func TestFileChangedNonExistent(t *testing.T) {
	if fileChanged(map[string][32]byte{}, "./does-not-exist") {
		t.Error("hash files generated hash for non-existent file")
	}
}
<|endoftext|>


<commit_before>package main

//import (
//	"testing"
//	"time"

//	. "github.com/aandryashin/matchers"
//	"github.com/fsnotify/fsnotify"
//)

//func TestSingleTimer(t *testing.T) {
//	watcher, _ := fsnotify.NewWatcher()
//	defer watcher.Close()
//	call := false
//	watch(watcher, 15*time.Millisecond, func() {
//		call = true
//	})
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}
//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{false})
//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{true})
//}

//func TestMultipleTimer(t *testing.T) {
//	watcher, _ := fsnotify.NewWatcher()
//	defer watcher.Close()

//	call := false
//	watch(watcher, 15*time.Millisecond, func() {
//		call = true
//	})
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{false})

//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{true})
//}

//func TestTimerCalledOnce(t *testing.T) {
//	watcher, _ := fsnotify.NewWatcher()
//	defer watcher.Close()

//	call := 0
//	watch(watcher, 20*time.Millisecond, func() {
//		call++
//	})
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, EqualTo{0})

//	<-time.After(20 * time.Millisecond)
//	AssertThat(t, call, EqualTo{1})
//}
<commit_msg>Trigger build on travis<commit_after>package main

import (
	"testing"
	"time"

	. "github.com/aandryashin/matchers"
	"github.com/fsnotify/fsnotify"
)

//func TestSingleTimer(t *testing.T) {
//	watcher, _ := fsnotify.NewWatcher()
//	defer watcher.Close()
//	call := false
//	watch(watcher, 15*time.Millisecond, func() {
//		call = true
//	})
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}
//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{false})
//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{true})
//}

//func TestMultipleTimer(t *testing.T) {
//	watcher, _ := fsnotify.NewWatcher()
//	defer watcher.Close()

//	call := false
//	watch(watcher, 15*time.Millisecond, func() {
//		call = true
//	})
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{false})

//	<-time.After(10 * time.Millisecond)
//	AssertThat(t, call, Is{true})
//}

func TestTimerCalledOnce(t *testing.T) {
	watcher, _ := fsnotify.NewWatcher()
	defer watcher.Close()

	call := 0
	watch(watcher, 20*time.Millisecond, func() {
		call++
	})
	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

	<-time.After(10 * time.Millisecond)
	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

	<-time.After(10 * time.Millisecond)
	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

	<-time.After(10 * time.Millisecond)
	watcher.Events <- fsnotify.Event{Op: fsnotify.Create}

	<-time.After(10 * time.Millisecond)
	AssertThat(t, call, EqualTo{0})

	<-time.After(20 * time.Millisecond)
	AssertThat(t, call, EqualTo{1})
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"encoding/json"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	fb "github.com/filebrowser/filebrowser/lib"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

var (
	cmdNotImplemented = []byte("Command not implemented.")
	cmdNotAllowed     = []byte("Command not allowed.")
)

// command handles the requests for VCS related commands: git, svn and mercurial
func command(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	// Upgrades the connection to a websocket and checks for fb.Errors.
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return 0, err
	}
	defer conn.Close()

	var (
		message []byte
		command []string
	)

	// Starts an infinite loop until a valid command is captured.
	for {
		_, message, err = conn.ReadMessage()
		if err != nil {
			return http.StatusInternalServerError, err
		}

		command = strings.Split(string(message), " ")
		if len(command) != 0 {
			break
		}
	}

	// Check if the command is allowed
	allowed := false

	for _, cmd := range c.User.Commands {
		if regexp.MustCompile(cmd).MatchString(command[0]) {
			allowed = true
			break
		}
	}

	if !allowed {
		err = conn.WriteMessage(websocket.TextMessage, cmdNotAllowed)
		if err != nil {
			return http.StatusInternalServerError, err
		}

		return 0, nil
	}

	// Check if the program is installed on the computer.
	if _, err = exec.LookPath(command[0]); err != nil {
		err = conn.WriteMessage(websocket.TextMessage, cmdNotImplemented)
		if err != nil {
			return http.StatusInternalServerError, err
		}

		return http.StatusNotImplemented, nil
	}

	// Gets the path and initializes a buffer.
	path := c.User.Scope + "/" + r.URL.Path
	path = filepath.Clean(path)
	buff := new(bytes.Buffer)

	// Sets up the command executation.
	cmd := exec.Command(command[0], command[1:]...)
	cmd.Dir = path
	cmd.Stderr = buff
	cmd.Stdout = buff

	// Starts the command and checks for fb.Errors.
	err = cmd.Start()
	if err != nil {
		return http.StatusInternalServerError, err
	}

	// Set a 'done' variable to check whetever the command has already finished
	// running or not. This verification is done using a goroutine that uses the
	// method .Wait() from the command.
	done := false
	go func() {
		err = cmd.Wait()
		done = true
	}()

	// Function to print the current information on the buffer to the connection.
	print := func() error {
		by := buff.Bytes()
		if len(by) > 0 {
			err = conn.WriteMessage(websocket.TextMessage, by)
			if err != nil {
				return err
			}
		}

		return nil
	}

	// While the command hasn't finished running, continue sending the output
	// to the client in intervals of 100 milliseconds.
	for !done {
		if err = print(); err != nil {
			return http.StatusInternalServerError, err
		}

		time.Sleep(100 * time.Millisecond)
	}

	// After the command is done executing, send the output one more time to the
	// browser to make sure it gets the latest information.
	if err = print(); err != nil {
		return http.StatusInternalServerError, err
	}

	return 0, nil
}

var (
	typeRegexp = regexp.MustCompile(`type:(\w+)`)
)

type condition func(path string) bool

type searchOptions struct {
	CaseSensitive bool
	Conditions    []condition
	Terms         []string
}

func extensionCondition(extension string) condition {
	return func(path string) bool {
		return filepath.Ext(path) == "."+extension
	}
}

func imageCondition(path string) bool {
	extension := filepath.Ext(path)
	mimetype := mime.TypeByExtension(extension)

	return strings.HasPrefix(mimetype, "image")
}

func audioCondition(path string) bool {
	extension := filepath.Ext(path)
	mimetype := mime.TypeByExtension(extension)

	return strings.HasPrefix(mimetype, "audio")
}

func videoCondition(path string) bool {
	extension := filepath.Ext(path)
	mimetype := mime.TypeByExtension(extension)

	return strings.HasPrefix(mimetype, "video")
}

func parseSearch(value string) *searchOptions {
	opts := &searchOptions{
		CaseSensitive: strings.Contains(value, "case:sensitive"),
		Conditions:    []condition{},
		Terms:         []string{},
	}

	// removes the options from the value
	value = strings.Replace(value, "case:insensitive", "", -1)
	value = strings.Replace(value, "case:sensitive", "", -1)
	value = strings.TrimSpace(value)

	types := typeRegexp.FindAllStringSubmatch(value, -1)
	for _, t := range types {
		if len(t) == 1 {
			continue
		}

		switch t[1] {
		case "image":
			opts.Conditions = append(opts.Conditions, imageCondition)
		case "audio", "music":
			opts.Conditions = append(opts.Conditions, audioCondition)
		case "video":
			opts.Conditions = append(opts.Conditions, videoCondition)
		default:
			opts.Conditions = append(opts.Conditions, extensionCondition(t[1]))
		}
	}

	if len(types) > 0 {
		// Remove the fields from the search value.
		value = typeRegexp.ReplaceAllString(value, "")
	}

	// If it's canse insensitive, put everything in lowercase.
	if !opts.CaseSensitive {
		value = strings.ToLower(value)
	}

	// Remove the spaces from the search value.
	value = strings.TrimSpace(value)

	if value == "" {
		return opts
	}

	// if the value starts with " and finishes what that character, we will
	// only search for that term
	if value[0] == '"' && value[len(value)-1] == '"' {
		unique := strings.TrimPrefix(value, "\"")
		unique = strings.TrimSuffix(unique, "\"")

		opts.Terms = []string{unique}
		return opts
	}

	opts.Terms = strings.Split(value, " ")
	return opts
}

// search searches for a file or directory.
func search(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	// Upgrades the connection to a websocket and checks for fb.Errors.
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return 0, err
	}
	defer conn.Close()

	var (
		value   string
		search  *searchOptions
		message []byte
	)

	// Starts an infinite loop until a valid command is captured.
	for {
		_, message, err = conn.ReadMessage()
		if err != nil {
			return http.StatusInternalServerError, err
		}

		if len(message) != 0 {
			value = string(message)
			break
		}
	}

	search = parseSearch(value)
	scope := strings.TrimPrefix(r.URL.Path, "/")
	scope = "/" + scope
	scope = c.User.Scope + scope
	scope = strings.Replace(scope, "\\", "/", -1)
	scope = filepath.Clean(scope)

	err = filepath.Walk(scope, func(path string, f os.FileInfo, err error) error {
		if !search.CaseSensitive {
			path = strings.ToLower(path)
		}

		path = strings.TrimPrefix(path, scope)
		path = strings.TrimPrefix(path, "/")
		path = strings.Replace(path, "\\", "/", -1)

		// Only execute if there are conditions to meet.
		if len(search.Conditions) > 0 {
			match := false

			for _, t := range search.Conditions {
				if t(path) {
					match = true
					break
				}
			}

			// If doesn't meet the condition, go to the next.
			if !match {
				return nil
			}
		}

		if len(search.Terms) > 0 {
			is := false

			// Checks if matches the terms and if it is allowed.
			for _, term := range search.Terms {
				if is {
					break
				}

				if strings.Contains(path, term) {
					if !c.User.Allowed(path) {
						return nil
					}

					is = true
				}
			}

			if !is {
				return nil
			}
		}

		response, _ := json.Marshal(map[string]interface{}{
			"dir":  f.IsDir(),
			"path": path,
		})

		return conn.WriteMessage(websocket.TextMessage, response)
	})

	if err != nil {
		return http.StatusInternalServerError, err
	}

	return 0, nil
}
<commit_msg>fix: search case sensitiveness (#515)<commit_after>package http

import (
	"bytes"
	"encoding/json"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	fb "github.com/filebrowser/filebrowser/lib"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

var (
	cmdNotImplemented = []byte("Command not implemented.")
	cmdNotAllowed     = []byte("Command not allowed.")
)

// command handles the requests for VCS related commands: git, svn and mercurial
func command(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	// Upgrades the connection to a websocket and checks for fb.Errors.
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return 0, err
	}
	defer conn.Close()

	var (
		message []byte
		command []string
	)

	// Starts an infinite loop until a valid command is captured.
	for {
		_, message, err = conn.ReadMessage()
		if err != nil {
			return http.StatusInternalServerError, err
		}

		command = strings.Split(string(message), " ")
		if len(command) != 0 {
			break
		}
	}

	// Check if the command is allowed
	allowed := false

	for _, cmd := range c.User.Commands {
		if regexp.MustCompile(cmd).MatchString(command[0]) {
			allowed = true
			break
		}
	}

	if !allowed {
		err = conn.WriteMessage(websocket.TextMessage, cmdNotAllowed)
		if err != nil {
			return http.StatusInternalServerError, err
		}

		return 0, nil
	}

	// Check if the program is installed on the computer.
	if _, err = exec.LookPath(command[0]); err != nil {
		err = conn.WriteMessage(websocket.TextMessage, cmdNotImplemented)
		if err != nil {
			return http.StatusInternalServerError, err
		}

		return http.StatusNotImplemented, nil
	}

	// Gets the path and initializes a buffer.
	path := c.User.Scope + "/" + r.URL.Path
	path = filepath.Clean(path)
	buff := new(bytes.Buffer)

	// Sets up the command executation.
	cmd := exec.Command(command[0], command[1:]...)
	cmd.Dir = path
	cmd.Stderr = buff
	cmd.Stdout = buff

	// Starts the command and checks for fb.Errors.
	err = cmd.Start()
	if err != nil {
		return http.StatusInternalServerError, err
	}

	// Set a 'done' variable to check whetever the command has already finished
	// running or not. This verification is done using a goroutine that uses the
	// method .Wait() from the command.
	done := false
	go func() {
		err = cmd.Wait()
		done = true
	}()

	// Function to print the current information on the buffer to the connection.
	print := func() error {
		by := buff.Bytes()
		if len(by) > 0 {
			err = conn.WriteMessage(websocket.TextMessage, by)
			if err != nil {
				return err
			}
		}

		return nil
	}

	// While the command hasn't finished running, continue sending the output
	// to the client in intervals of 100 milliseconds.
	for !done {
		if err = print(); err != nil {
			return http.StatusInternalServerError, err
		}

		time.Sleep(100 * time.Millisecond)
	}

	// After the command is done executing, send the output one more time to the
	// browser to make sure it gets the latest information.
	if err = print(); err != nil {
		return http.StatusInternalServerError, err
	}

	return 0, nil
}

var (
	typeRegexp = regexp.MustCompile(`type:(\w+)`)
)

type condition func(path string) bool

type searchOptions struct {
	CaseSensitive bool
	Conditions    []condition
	Terms         []string
}

func extensionCondition(extension string) condition {
	return func(path string) bool {
		return filepath.Ext(path) == "."+extension
	}
}

func imageCondition(path string) bool {
	extension := filepath.Ext(path)
	mimetype := mime.TypeByExtension(extension)

	return strings.HasPrefix(mimetype, "image")
}

func audioCondition(path string) bool {
	extension := filepath.Ext(path)
	mimetype := mime.TypeByExtension(extension)

	return strings.HasPrefix(mimetype, "audio")
}

func videoCondition(path string) bool {
	extension := filepath.Ext(path)
	mimetype := mime.TypeByExtension(extension)

	return strings.HasPrefix(mimetype, "video")
}

func parseSearch(value string) *searchOptions {
	opts := &searchOptions{
		CaseSensitive: strings.Contains(value, "case:sensitive"),
		Conditions:    []condition{},
		Terms:         []string{},
	}

	// removes the options from the value
	value = strings.Replace(value, "case:insensitive", "", -1)
	value = strings.Replace(value, "case:sensitive", "", -1)
	value = strings.TrimSpace(value)

	types := typeRegexp.FindAllStringSubmatch(value, -1)
	for _, t := range types {
		if len(t) == 1 {
			continue
		}

		switch t[1] {
		case "image":
			opts.Conditions = append(opts.Conditions, imageCondition)
		case "audio", "music":
			opts.Conditions = append(opts.Conditions, audioCondition)
		case "video":
			opts.Conditions = append(opts.Conditions, videoCondition)
		default:
			opts.Conditions = append(opts.Conditions, extensionCondition(t[1]))
		}
	}

	if len(types) > 0 {
		// Remove the fields from the search value.
		value = typeRegexp.ReplaceAllString(value, "")
	}

	// If it's canse insensitive, put everything in lowercase.
	if !opts.CaseSensitive {
		value = strings.ToLower(value)
	}

	// Remove the spaces from the search value.
	value = strings.TrimSpace(value)

	if value == "" {
		return opts
	}

	// if the value starts with " and finishes what that character, we will
	// only search for that term
	if value[0] == '"' && value[len(value)-1] == '"' {
		unique := strings.TrimPrefix(value, "\"")
		unique = strings.TrimSuffix(unique, "\"")

		opts.Terms = []string{unique}
		return opts
	}

	opts.Terms = strings.Split(value, " ")
	return opts
}

// search searches for a file or directory.
func search(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	// Upgrades the connection to a websocket and checks for fb.Errors.
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return 0, err
	}
	defer conn.Close()

	var (
		value   string
		search  *searchOptions
		message []byte
	)

	// Starts an infinite loop until a valid command is captured.
	for {
		_, message, err = conn.ReadMessage()
		if err != nil {
			return http.StatusInternalServerError, err
		}

		if len(message) != 0 {
			value = string(message)
			break
		}
	}

	search = parseSearch(value)
	scope := strings.TrimPrefix(r.URL.Path, "/")
	scope = "/" + scope
	scope = c.User.Scope + scope
	scope = strings.Replace(scope, "\\", "/", -1)
	scope = filepath.Clean(scope)

	err = filepath.Walk(scope, func(path string, f os.FileInfo, err error) error {
		var (
			originalPath string
		)

		path = strings.TrimPrefix(path, scope)
		path = strings.TrimPrefix(path, "/")
		path = strings.Replace(path, "\\", "/", -1)

		originalPath = path

		if !search.CaseSensitive {
			path = strings.ToLower(path)
		}

		// Only execute if there are conditions to meet.
		if len(search.Conditions) > 0 {
			match := false

			for _, t := range search.Conditions {
				if t(path) {
					match = true
					break
				}
			}

			// If doesn't meet the condition, go to the next.
			if !match {
				return nil
			}
		}

		if len(search.Terms) > 0 {
			is := false

			// Checks if matches the terms and if it is allowed.
			for _, term := range search.Terms {
				if is {
					break
				}

				if strings.Contains(path, term) {
					if !c.User.Allowed(path) {
						return nil
					}

					is = true
				}
			}

			if !is {
				return nil
			}
		}

		response, _ := json.Marshal(map[string]interface{}{
			"dir":  f.IsDir(),
			"path": originalPath,
		})

		return conn.WriteMessage(websocket.TextMessage, response)
	})

	if err != nil {
		return http.StatusInternalServerError, err
	}

	return 0, nil
}
<|endoftext|>