<commit_before>// $G $F.go && $L $F.$A && ./$A.out

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

package main

import (
	"fmt"
	"math"
	"strings"
	"syscall"
)

type Error interface {
	String() string
}

type ErrorTest struct {
	name	string
	fn	func()
	err	string
}

var (
	i, j, k int = 0, 0, 1
	i8, j8, k8 int8 = 0, 0, 1
	i16, j16, k16 int16 = 0, 0, 1
	i32, j32, k32 int32 = 0, 0, 1
	i64, j64, k64 int64 = 0, 0, 1

	u, v, w uint = 0, 0, 1
	u8, v8, w8 uint8 = 0, 0, 1
	u16, v16, w16 uint16 = 0, 0, 1
	u32, v32, w32 uint32 = 0, 0, 1
	u64, v64, w64 uint64 = 0, 0, 1
	up, vp, wp uintptr = 0, 0, 1

	f, g, h float = 0, 0, 1
	f32, g32, h32 float32 = 0, 0, 1
	f64, g64, h64, inf, negInf, nan float64 = 0, 0, 1, math.Inf(1), math.Inf(-1), math.NaN()

	c, d, e complex = 0+0i, 0+0i, 1+1i
	c64, d64, e64 complex64 = 0+0i, 0+0i, 1+1i
	c128, d128, e128 complex128 = 0+0i, 0+0i, 1+1i
)

var tmp interface{}

// We could assign to _ but the compiler optimizes it too easily.
func use(v interface{}) {
	tmp = v
}

// Verify error/no error for all types.
var errorTests = []ErrorTest{
	// All integer divide by zero should error.
	ErrorTest{ "int 0/0", func() { use(i/j) }, "divide", },
	ErrorTest{ "int8 0/0", func() { use(i8/j8) }, "divide", },
	ErrorTest{ "int16 0/0", func() { use(i16/j16) }, "divide", },
	ErrorTest{ "int32 0/0", func() { use(i32/j32) }, "divide", },
	ErrorTest{ "int64 0/0", func() { use(i64/j64) }, "divide", },

	ErrorTest{ "int 1/0", func() { use(k/j) }, "divide", },
	ErrorTest{ "int8 1/0", func() { use(k8/j8) }, "divide", },
	ErrorTest{ "int16 1/0", func() { use(k16/j16) }, "divide", },
	ErrorTest{ "int32 1/0", func() { use(k32/j32) }, "divide", },
	ErrorTest{ "int64 1/0", func() { use(k64/j64) }, "divide", },

	ErrorTest{ "uint 0/0", func() { use(u/v) }, "divide", },
	ErrorTest{ "uint8 0/0", func() { use(u8/v8) }, "divide", },
	ErrorTest{ "uint16 0/0", func() { use(u16/v16) }, "divide", },
	ErrorTest{ "uint32 0/0", func() { use(u32/v32) }, "divide", },
	ErrorTest{ "uint64 0/0", func() { use(u64/v64) }, "divide", },
	ErrorTest{ "uintptr 0/0", func() { use(up/vp) }, "divide", },

	ErrorTest{ "uint 1/0", func() { use(w/v) }, "divide", },
	ErrorTest{ "uint8 1/0", func() { use(w8/v8) }, "divide", },
	ErrorTest{ "uint16 1/0", func() { use(w16/v16) }, "divide", },
	ErrorTest{ "uint32 1/0", func() { use(w32/v32) }, "divide", },
	ErrorTest{ "uint64 1/0", func() { use(w64/v64) }, "divide", },
	ErrorTest{ "uintptr 1/0", func() { use(wp/vp) }, "divide", },

	// All floating divide by zero should not error.
	ErrorTest{ "float 0/0", func() { use(f/g) }, "", },
	ErrorTest{ "float32 0/0", func() { use(f32/g32) }, "", },
	ErrorTest{ "float64 0/0", func() { use(f64/g64) }, "", },

	ErrorTest{ "float 1/0", func() { use(h/g) }, "", },
	ErrorTest{ "float32 1/0", func() { use(h32/g32) }, "", },
	ErrorTest{ "float64 1/0", func() { use(h64/g64) }, "", },
	ErrorTest{ "float64 inf/0", func() { use(inf/g64) }, "", },
	ErrorTest{ "float64 -inf/0", func() { use(negInf/g64) }, "", },
	ErrorTest{ "float64 nan/0", func() { use(nan/g64) }, "", },

	// All complex divide by zero should not error.
	ErrorTest{ "complex 0/0", func() { use(c/d) }, "", },
	ErrorTest{ "complex64 0/0", func() { use(c64/d64) }, "", },
	ErrorTest{ "complex128 0/0", func() { use(c128/d128) }, "", },

	ErrorTest{ "complex 1/0", func() { use(e/d) }, "", },
	ErrorTest{ "complex64 1/0", func() { use(e64/d64) }, "", },
	ErrorTest{ "complex128 1/0", func() { use(e128/d128) }, "", },
}

func error(fn func()) (error string) {
	defer func() {
		if e := recover(); e != nil {
			error = e.(Error).String()
		}
	}()
	fn()
	return ""
}

type FloatTest struct{
	f, g	float64
	out	float64
}

var floatTests = []FloatTest{
	FloatTest{0, 0, nan},
	FloatTest{nan, 0, nan},
	FloatTest{inf, 0, inf},
	FloatTest{negInf, 0, negInf},
}

func alike(a, b float64) bool {
	switch {
	case math.IsNaN(a) && math.IsNaN(b):
		return true
	case a == b:
		return math.Signbit(a) == math.Signbit(b)
	}
	return false
}

func main() {
	bad := false
	for _, t := range errorTests {
		if t.err != "" && syscall.OS == "nacl" {
			continue
		}
		err := error(t.fn)
		switch {
		case t.err == "" && err == "":
			// fine
		case t.err != "" && err == "":
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			fmt.Printf("%s: expected %q; got no error\n", t.name, t.err)
		case t.err == "" && err != "":
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			fmt.Printf("%s: expected no error; got %q\n", t.name, err)
		case t.err != "" && err != "":
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			if strings.Index(err, t.err) < 0 {
				fmt.Printf("%s: expected %q; got %q\n", t.name, t.err, err)
				continue
			}
		}
	}

	// At this point we know we don't error on the values we're testing
	for _, t := range floatTests {
		x := t.f/t.g
		if !alike(x, t.out) {
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			fmt.Printf("%v/%v: expected %g error; got %g\n", t.f, t.g, t.out, x)
		}
	}
}
<commit_msg>fix build<commit_after>// $G $F.go && $L $F.$A && ./$A.out

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

package main

import (
	"fmt"
	"math"
	"strings"
	"syscall"
)

type Error interface {
	String() string
}

type ErrorTest struct {
	name	string
	fn	func()
	err	string
}

var (
	i, j, k int = 0, 0, 1
	i8, j8, k8 int8 = 0, 0, 1
	i16, j16, k16 int16 = 0, 0, 1
	i32, j32, k32 int32 = 0, 0, 1
	i64, j64, k64 int64 = 0, 0, 1

	u, v, w uint = 0, 0, 1
	u8, v8, w8 uint8 = 0, 0, 1
	u16, v16, w16 uint16 = 0, 0, 1
	u32, v32, w32 uint32 = 0, 0, 1
	u64, v64, w64 uint64 = 0, 0, 1
	up, vp, wp uintptr = 0, 0, 1

	f, g, h float = 0, 0, 1
	f32, g32, h32 float32 = 0, 0, 1
	f64, g64, h64, inf, negInf, nan float64 = 0, 0, 1, math.Inf(1), math.Inf(-1), math.NaN()

	c, d, e complex = 0+0i, 0+0i, 1+1i
	c64, d64, e64 complex64 = 0+0i, 0+0i, 1+1i
	c128, d128, e128 complex128 = 0+0i, 0+0i, 1+1i
)

var tmp interface{}

// We could assign to _ but the compiler optimizes it too easily.
func use(v interface{}) {
	tmp = v
}

// Verify error/no error for all types.
var errorTests = []ErrorTest{
	// All integer divide by zero should error.
	ErrorTest{ "int 0/0", func() { use(i/j) }, "divide", },
	ErrorTest{ "int8 0/0", func() { use(i8/j8) }, "divide", },
	ErrorTest{ "int16 0/0", func() { use(i16/j16) }, "divide", },
	ErrorTest{ "int32 0/0", func() { use(i32/j32) }, "divide", },
	ErrorTest{ "int64 0/0", func() { use(i64/j64) }, "divide", },

	ErrorTest{ "int 1/0", func() { use(k/j) }, "divide", },
	ErrorTest{ "int8 1/0", func() { use(k8/j8) }, "divide", },
	ErrorTest{ "int16 1/0", func() { use(k16/j16) }, "divide", },
	ErrorTest{ "int32 1/0", func() { use(k32/j32) }, "divide", },
	ErrorTest{ "int64 1/0", func() { use(k64/j64) }, "divide", },

	ErrorTest{ "uint 0/0", func() { use(u/v) }, "divide", },
	ErrorTest{ "uint8 0/0", func() { use(u8/v8) }, "divide", },
	ErrorTest{ "uint16 0/0", func() { use(u16/v16) }, "divide", },
	ErrorTest{ "uint32 0/0", func() { use(u32/v32) }, "divide", },
	ErrorTest{ "uint64 0/0", func() { use(u64/v64) }, "divide", },
	ErrorTest{ "uintptr 0/0", func() { use(up/vp) }, "divide", },

	ErrorTest{ "uint 1/0", func() { use(w/v) }, "divide", },
	ErrorTest{ "uint8 1/0", func() { use(w8/v8) }, "divide", },
	ErrorTest{ "uint16 1/0", func() { use(w16/v16) }, "divide", },
	ErrorTest{ "uint32 1/0", func() { use(w32/v32) }, "divide", },
	ErrorTest{ "uint64 1/0", func() { use(w64/v64) }, "divide", },
	ErrorTest{ "uintptr 1/0", func() { use(wp/vp) }, "divide", },

	// All floating divide by zero should not error.
	ErrorTest{ "float 0/0", func() { use(f/g) }, "", },
	ErrorTest{ "float32 0/0", func() { use(f32/g32) }, "", },
	ErrorTest{ "float64 0/0", func() { use(f64/g64) }, "", },

	ErrorTest{ "float 1/0", func() { use(h/g) }, "", },
	ErrorTest{ "float32 1/0", func() { use(h32/g32) }, "", },
	ErrorTest{ "float64 1/0", func() { use(h64/g64) }, "", },
	ErrorTest{ "float64 inf/0", func() { use(inf/g64) }, "", },
	ErrorTest{ "float64 -inf/0", func() { use(negInf/g64) }, "", },
	ErrorTest{ "float64 nan/0", func() { use(nan/g64) }, "", },

	// All complex divide by zero should not error.
	ErrorTest{ "complex 0/0", func() { use(c/d) }, "", },
	ErrorTest{ "complex64 0/0", func() { use(c64/d64) }, "", },
	ErrorTest{ "complex128 0/0", func() { use(c128/d128) }, "", },

	ErrorTest{ "complex 1/0", func() { use(e/d) }, "", },
	ErrorTest{ "complex64 1/0", func() { use(e64/d64) }, "", },
	ErrorTest{ "complex128 1/0", func() { use(e128/d128) }, "", },
}

func error(fn func()) (error string) {
	defer func() {
		if e := recover(); e != nil {
			error = e.(Error).String()
		}
	}()
	fn()
	return ""
}

type FloatTest struct{
	f, g	float64
	out	float64
}

var floatTests = []FloatTest{
	FloatTest{0, 0, nan},
	FloatTest{nan, 0, nan},
	FloatTest{inf, 0, inf},
	FloatTest{negInf, 0, negInf},
}

func alike(a, b float64) bool {
	switch {
	case math.IsNaN(a) && math.IsNaN(b):
		return true
	case a == b:
		return math.Signbit(a) == math.Signbit(b)
	}
	return false
}

func main() {
	bad := false
	for _, t := range errorTests {
		if t.err != "" && syscall.OS == "nacl" {
			continue
		}
		err := error(t.fn)
		switch {
		case t.err == "" && err == "":
			// fine
		case t.err != "" && err == "":
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			fmt.Printf("%s: expected %q; got no error\n", t.name, t.err)
		case t.err == "" && err != "":
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			fmt.Printf("%s: expected no error; got %q\n", t.name, err)
		case t.err != "" && err != "":
			if strings.Index(err, t.err) < 0 {
				if !bad {
					bad = true
					fmt.Printf("BUG\n")
				}
				fmt.Printf("%s: expected %q; got %q\n", t.name, t.err, err)
				continue
			}
		}
	}

	// At this point we know we don't error on the values we're testing
	for _, t := range floatTests {
		x := t.f/t.g
		if !alike(x, t.out) {
			if !bad {
				bad = true
				fmt.Printf("BUG\n")
			}
			fmt.Printf("%v/%v: expected %g error; got %g\n", t.f, t.g, t.out, x)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"os"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec             chan *LogRecord
	rot             chan bool
	discardWhenBusy bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate bool
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	if w.discardWhenBusy {
		select {
		case w.rec <- rec:
		default:
			// busy, maybe disk full
		}
	} else {
		w.rec <- rec
	}
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool, discardWhenBusy bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:             make(chan *LogRecord, LogBufferLength),
		rot:             make(chan bool),
		filename:        fname,
		format:          "[%D %T] [%L] (%S) %M",
		rotate:          rotate,
		discardWhenBusy: discardWhenBusy,
	}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
					(w.daily && now.Day() != w.daily_opendate) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			num := 1
			fname := ""
			for ; err == nil && num <= 999; num++ {
				fname = w.filename + fmt.Sprintf(".%03d", num)
				_, err = os.Lstat(fname)
			}
			// return error if the last file checked still existed
			if err == nil {
				return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			}

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate, false).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
<commit_msg>filelogger will merge continous log messages<commit_after>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"os"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec             chan *LogRecord
	rot             chan bool
	discardWhenBusy bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate bool
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	if w.discardWhenBusy {
		select {
		case w.rec <- rec:
		default:
			// busy, maybe disk full
		}
	} else {
		w.rec <- rec
	}
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool, discardWhenBusy bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:             make(chan *LogRecord, LogBufferLength),
		rot:             make(chan bool),
		filename:        fname,
		format:          "[%D %T] [%L] (%S) %M",
		rotate:          rotate,
		discardWhenBusy: discardWhenBusy,
	}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		var (
			lastRec       LogRecord
			lastRepeatedN int
		)
		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

			case rec, ok := <-w.rec:
				if !ok {
					return
				}

				if lastRec.Source == rec.Source && lastRec.Message == rec.Message {
					lastRepeatedN++
					continue
				}

				now := time.Now() // TODO do we need this? we have rec.Created
				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
					(w.daily && now.Day() != w.daily_opendate) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				if lastRepeatedN > 0 {
					lastRec.Message = fmt.Sprintf("%d times: %s", lastRepeatedN, lastRec.Message)
					if _, err := fmt.Fprint(w.file, FormatLogRecord(w.format, &lastRec)); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				lastRepeatedN = 0
				lastRec = *rec

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			num := 1
			fname := ""
			for ; err == nil && num <= 999; num++ {
				fname = w.filename + fmt.Sprintf(".%03d", num)
				_, err = os.Lstat(fname)
			}
			// return error if the last file checked still existed
			if err == nil {
				return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			}

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate, false).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
<|endoftext|>


<commit_before>package emitter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"code.cloudfoundry.org/lager"
	"github.com/concourse/concourse/atc/metric"
	"github.com/pkg/errors"
)

type (
	stats struct {
		created interface{}
		deleted interface{}
	}

	NewRelicEmitter struct {
		client     *http.Client
		url        string
		apikey     string
		prefix     string
		containers *stats
		volumes    *stats
	}

	NewRelicConfig struct {
		AccountID     string `long:"newrelic-account-id" description:"New Relic Account ID"`
		APIKey        string `long:"newrelic-api-key" description:"New Relic Insights API Key"`
		ServicePrefix string `long:"newrelic-service-prefix" default:"" description:"An optional prefix for emitted New Relic events"`
	}

	singlePayload map[string]interface{}
	fullPayload   []singlePayload
)

func init() {
	metric.RegisterEmitter(&NewRelicConfig{})
}

func (config *NewRelicConfig) Description() string { return "NewRelic" }
func (config *NewRelicConfig) IsConfigured() bool {
	return config.AccountID != "" && config.APIKey != ""
}

func (config *NewRelicConfig) NewEmitter() (metric.Emitter, error) {
	client := &http.Client{
		Transport: &http.Transport{},
		Timeout:   time.Minute,
	}

	return &NewRelicEmitter{
		client:     client,
		url:        fmt.Sprintf("https://insights-collector.newrelic.com/v1/accounts/%s/events", config.AccountID),
		apikey:     config.APIKey,
		prefix:     config.ServicePrefix,
		containers: new(stats),
		volumes:    new(stats),
	}, nil
}

func (emitter *NewRelicEmitter) simplePayload(logger lager.Logger, event metric.Event, nameOverride string) singlePayload {
	name := nameOverride
	if name == "" {
		name = strings.Replace(event.Name, " ", "_", -1)
	}

	eventType := fmt.Sprintf("%s%s", emitter.prefix, name)

	payload := singlePayload{
		"eventType": eventType,
		"value":     event.Value,
		"state":     string(event.State),
		"host":      event.Host,
		"timestamp": event.Time.Unix(),
	}

	for k, v := range event.Attributes {
		payload[fmt.Sprintf("_%s", k)] = v
	}
	return payload
}

func (emitter *NewRelicEmitter) emitPayload(logger lager.Logger, payload fullPayload) {
	payloadJSON, err := json.Marshal(payload)
	if err != nil {
		logger.Error("failed-to-serialize-payload", err)
		return
	}

	req, err := http.NewRequest("POST", emitter.url, bytes.NewBuffer(payloadJSON))
	if err != nil {
		logger.Error("failed-to-construct-request", err)
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("X-Insert-Key", emitter.apikey)

	resp, err := emitter.client.Do(req)

	if err != nil {
		logger.Error("failed-to-send-request",
			errors.Wrap(metric.ErrFailedToEmit, err.Error()))
		return
	}

	resp.Body.Close()
}

func (emitter *NewRelicEmitter) Emit(logger lager.Logger, event metric.Event) {
	payload := make(fullPayload, 0)

	switch event.Name {

	// These are the simple ones that only need a small name transformation
	case "build started",
		"build finished",
		"worker containers",
		"worker volumes",
		"http response time",
		"database queries",
		"database connections":
		payload = append(payload, emitter.simplePayload(logger, event, ""))

	// These are periodic metrics that are consolidated and only emitted once
	// per cycle (the emit trigger is chosen because it's currently last in the
	// periodic list, so we should have a coherent view). We do this because
	// new relic has a hard limit on the total number of metrics in a 24h
	// period, so batching similar data where possible makes sense.
	case "containers deleted":
		emitter.containers.deleted = event.Value
	case "containers created":
		emitter.containers.created = event.Value
	case "failed containers":
		newPayload := emitter.simplePayload(logger, event, "containers")
		newPayload["failed"] = newPayload["value"]
		newPayload["created"] = emitter.containers.created
		newPayload["deleted"] = emitter.containers.deleted
		delete(newPayload, "value")
		payload = append(payload, newPayload)

	case "volumes deleted":
		emitter.volumes.deleted = event.Value
	case "volumes created":
		emitter.volumes.created = event.Value
	case "failed volumes":
		newPayload := emitter.simplePayload(logger, event, "volumes")
		newPayload["failed"] = newPayload["value"]
		newPayload["created"] = emitter.volumes.created
		newPayload["deleted"] = emitter.volumes.deleted
		delete(newPayload, "value")
		payload = append(payload, newPayload)

	// And a couple that need a small rename (new relic doesn't like some chars)
	case "scheduling: full duration (ms)":
		payload = append(payload, emitter.simplePayload(logger, event, "scheduling_full_duration_ms"))
	case "scheduling: loading versions duration (ms)":
		payload = append(payload, emitter.simplePayload(logger, event, "scheduling_load_duration_ms"))
	case "scheduling: job duration (ms)":
		payload = append(payload, emitter.simplePayload(logger, event, "scheduling_job_duration_ms"))
	default:
		// Ignore the rest
	}

	// But also log any metric that's not EventStateOK, even if we're not
	// otherwise recording it. (This won't be easily graphable, that's okay,
	// this is more for monitoring synthetics)
	if event.State != metric.EventStateOK {
		singlePayload := emitter.simplePayload(logger, event, "alert")
		// We don't have friendly names for all the metrics, and part of the
		// point of this alert is to catch events we should be logging but
		// didn't; therefore, be consistently inconsistent and use the
		// concourse metric names, not our translation layer.
		singlePayload["metric"] = event.Name
		payload = append(payload, singlePayload)
	}

	if len(payload) > 0 {
		emitter.emitPayload(logger, payload)
	}
}
<commit_msg>adding newrelic unknown containers/volumes metric<commit_after>package emitter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"code.cloudfoundry.org/lager"
	"github.com/concourse/concourse/atc/metric"
	"github.com/pkg/errors"
)

type (
	stats struct {
		created interface{}
		deleted interface{}
		unknown interface{}
	}

	NewRelicEmitter struct {
		client     *http.Client
		url        string
		apikey     string
		prefix     string
		containers *stats
		volumes    *stats
	}

	NewRelicConfig struct {
		AccountID     string `long:"newrelic-account-id" description:"New Relic Account ID"`
		APIKey        string `long:"newrelic-api-key" description:"New Relic Insights API Key"`
		ServicePrefix string `long:"newrelic-service-prefix" default:"" description:"An optional prefix for emitted New Relic events"`
	}

	singlePayload map[string]interface{}
	fullPayload   []singlePayload
)

func init() {
	metric.RegisterEmitter(&NewRelicConfig{})
}

func (config *NewRelicConfig) Description() string { return "NewRelic" }
func (config *NewRelicConfig) IsConfigured() bool {
	return config.AccountID != "" && config.APIKey != ""
}

func (config *NewRelicConfig) NewEmitter() (metric.Emitter, error) {
	client := &http.Client{
		Transport: &http.Transport{},
		Timeout:   time.Minute,
	}

	return &NewRelicEmitter{
		client:     client,
		url:        fmt.Sprintf("https://insights-collector.newrelic.com/v1/accounts/%s/events", config.AccountID),
		apikey:     config.APIKey,
		prefix:     config.ServicePrefix,
		containers: new(stats),
		volumes:    new(stats),
	}, nil
}

func (emitter *NewRelicEmitter) simplePayload(logger lager.Logger, event metric.Event, nameOverride string) singlePayload {
	name := nameOverride
	if name == "" {
		name = strings.Replace(event.Name, " ", "_", -1)
	}

	eventType := fmt.Sprintf("%s%s", emitter.prefix, name)

	payload := singlePayload{
		"eventType": eventType,
		"value":     event.Value,
		"state":     string(event.State),
		"host":      event.Host,
		"timestamp": event.Time.Unix(),
	}

	for k, v := range event.Attributes {
		payload[fmt.Sprintf("_%s", k)] = v
	}
	return payload
}

func (emitter *NewRelicEmitter) emitPayload(logger lager.Logger, payload fullPayload) {
	payloadJSON, err := json.Marshal(payload)
	if err != nil {
		logger.Error("failed-to-serialize-payload", err)
		return
	}

	req, err := http.NewRequest("POST", emitter.url, bytes.NewBuffer(payloadJSON))
	if err != nil {
		logger.Error("failed-to-construct-request", err)
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("X-Insert-Key", emitter.apikey)

	resp, err := emitter.client.Do(req)

	if err != nil {
		logger.Error("failed-to-send-request",
			errors.Wrap(metric.ErrFailedToEmit, err.Error()))
		return
	}

	resp.Body.Close()
}

func (emitter *NewRelicEmitter) Emit(logger lager.Logger, event metric.Event) {
	payload := make(fullPayload, 0)

	switch event.Name {

	// These are the simple ones that only need a small name transformation
	case "build started",
		"build finished",
		"worker containers",
		"worker volumes",
		"http response time",
		"database queries",
		"database connections":
		payload = append(payload, emitter.simplePayload(logger, event, ""))

	// These are periodic metrics that are consolidated and only emitted once
	// per cycle (the emit trigger is chosen because it's currently last in the
	// periodic list, so we should have a coherent view). We do this because
	// new relic has a hard limit on the total number of metrics in a 24h
	// period, so batching similar data where possible makes sense.
	case "containers deleted":
		emitter.containers.deleted = event.Value
	case "containers created":
		emitter.containers.created = event.Value
	case "unknown containers":
		emitter.containers.unknown = event.Value
	case "failed containers":
		newPayload := emitter.simplePayload(logger, event, "containers")
		newPayload["failed"] = newPayload["value"]
		newPayload["created"] = emitter.containers.created
		newPayload["deleted"] = emitter.containers.deleted
		delete(newPayload, "value")
		payload = append(payload, newPayload)

	case "volumes deleted":
		emitter.volumes.deleted = event.Value
	case "volumes created":
		emitter.volumes.created = event.Value
	case "unknown volumes":
		emitter.volumes.unknown = event.Value
	case "failed volumes":
		newPayload := emitter.simplePayload(logger, event, "volumes")
		newPayload["failed"] = newPayload["value"]
		newPayload["created"] = emitter.volumes.created
		newPayload["deleted"] = emitter.volumes.deleted
		delete(newPayload, "value")
		payload = append(payload, newPayload)

	// And a couple that need a small rename (new relic doesn't like some chars)
	case "scheduling: full duration (ms)":
		payload = append(payload, emitter.simplePayload(logger, event, "scheduling_full_duration_ms"))
	case "scheduling: loading versions duration (ms)":
		payload = append(payload, emitter.simplePayload(logger, event, "scheduling_load_duration_ms"))
	case "scheduling: job duration (ms)":
		payload = append(payload, emitter.simplePayload(logger, event, "scheduling_job_duration_ms"))
	default:
		// Ignore the rest
	}

	// But also log any metric that's not EventStateOK, even if we're not
	// otherwise recording it. (This won't be easily graphable, that's okay,
	// this is more for monitoring synthetics)
	if event.State != metric.EventStateOK {
		singlePayload := emitter.simplePayload(logger, event, "alert")
		// We don't have friendly names for all the metrics, and part of the
		// point of this alert is to catch events we should be logging but
		// didn't; therefore, be consistently inconsistent and use the
		// concourse metric names, not our translation layer.
		singlePayload["metric"] = event.Name
		payload = append(payload, singlePayload)
	}

	if len(payload) > 0 {
		emitter.emitPayload(logger, payload)
	}
}
<|endoftext|>


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

// This package provides a single function, Do, to run a function
// exactly once, usually used as part of initialization.
package once

import "sync"

type job struct {
	done bool;
	sync.Mutex;	// should probably be sync.Notification or some such
}

var jobs = make(map[func()]*job)
var joblock sync.Mutex;

// Do is the the only exported piece of the package.
// For one-time initialization that is not done during init,
// wrap the initialization in a niladic function f() and call
//	Do(f)
// If multiple processes call Do(f) simultaneously
// with the same f argument, only one will call f, and the
// others will block until f finishes running.
func Do(f func()) {
	joblock.Lock();
	j, present := jobs[f];
	if !present {
		// run it
		j = new(job);
		j.Lock();
		jobs[f] = j;
		joblock.Unlock();
		f();
		j.done = true;
		j.Unlock();
	} else {
		// wait for it
		joblock.Unlock();
		if j.done != true {
			j.Lock();
			j.Unlock();
		}
	}
}
<commit_msg>add note about once and closures<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package provides a single function, Do, to run a function
// exactly once, usually used as part of initialization.
package once

import "sync"

type job struct {
	done bool;
	sync.Mutex;	// should probably be sync.Notification or some such
}

var jobs = make(map[func()]*job)
var joblock sync.Mutex;

// Do is the the only exported piece of the package.
// For one-time initialization that is not done during init,
// wrap the initialization in a niladic function f() and call
//	Do(f)
// If multiple processes call Do(f) simultaneously
// with the same f argument, only one will call f, and the
// others will block until f finishes running.
//
// Since a func() expression typically evaluates to a differerent
// function value each time it is evaluated, it is incorrect to
// pass such values to Do.  For example,
// 	func f(x int) {
//		Do(func() { fmt.Println(x) })
//	}
// behaves the same as
//	func f(x int) {
//		fmt.Println(x)
//	}
// because the func() expression in the first creates a new
// func each time f runs, and each of those funcs is run once.
func Do(f func()) {
	joblock.Lock();
	j, present := jobs[f];
	if !present {
		// run it
		j = new(job);
		j.Lock();
		jobs[f] = j;
		joblock.Unlock();
		f();
		j.done = true;
		j.Unlock();
	} else {
		// wait for it
		joblock.Unlock();
		if j.done != true {
			j.Lock();
			j.Unlock();
		}
	}
}
<|endoftext|>


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

package gob

import (
	"fmt"
	"os"
	"reflect"
	"sync"
)

type kind reflect.Type

// Reflection types are themselves interface values holding structs
// describing the type.  Each type has a different struct so that struct can
// be the kind.  For example, if typ is the reflect type for an int8, typ is
// a pointer to a reflect.Int8Type struct; if typ is the reflect type for a
// function, typ is a pointer to a reflect.FuncType struct; we use the type
// of that pointer as the kind.

// typeKind returns a reflect.Type representing typ's kind.  The kind is the
// general kind of type:
//	int8, int16, int, uint, float, func, chan, struct, and so on.
// That is, all struct types have the same kind, all func types have the same
// kind, all int8 types have the same kind, and so on.
func typeKind(typ reflect.Type) kind { return kind(reflect.Typeof(typ)) }

// valueKind returns the kind of the value type
// stored inside the interface v.
func valueKind(v interface{}) reflect.Type { return typeKind(reflect.Typeof(v)) }

// A typeId represents a gob Type as an integer that can be passed on the wire.
// Internally, typeIds are used as keys to a map to recover the underlying type info.
type typeId int32

var nextId typeId       // incremented for each new type we build
var typeLock sync.Mutex // set while building a type

type gobType interface {
	id() typeId
	setId(id typeId)
	Name() string
	string() string // not public; only for debugging
	safeString(seen map[typeId]bool) string
}

var types = make(map[reflect.Type]gobType)
var idToType = make(map[typeId]gobType)
var builtinIdToType map[typeId]gobType // set in init() after builtins are established

func setTypeId(typ gobType) {
	nextId++
	typ.setId(nextId)
	idToType[nextId] = typ
}

func (t typeId) gobType() gobType {
	if t == 0 {
		return nil
	}
	return idToType[t]
}

// string returns the string representation of the type associated with the typeId.
func (t typeId) string() string { return t.gobType().string() }

// Name returns the name of the type associated with the typeId.
func (t typeId) Name() string { return t.gobType().Name() }

// Common elements of all types.
type commonType struct {
	name string
	_id  typeId
}

func (t *commonType) id() typeId { return t._id }

func (t *commonType) setId(id typeId) { t._id = id }

func (t *commonType) string() string { return t.name }

func (t *commonType) safeString(seen map[typeId]bool) string {
	return t.name
}

func (t *commonType) Name() string { return t.name }

// Create and check predefined types
// The string for tBytes is "bytes" not "[]byte" to signify its specialness.

var tBool = bootstrapType("bool", false, 1)
var tInt = bootstrapType("int", int(0), 2)
var tUint = bootstrapType("uint", uint(0), 3)
var tFloat = bootstrapType("float", float64(0), 4)
var tBytes = bootstrapType("bytes", make([]byte, 0), 5)
var tString = bootstrapType("string", "", 6)

// Predefined because it's needed by the Decoder
var tWireType = mustGetTypeInfo(reflect.Typeof(wireType{})).id

func init() {
	checkId(7, tWireType)
	checkId(9, mustGetTypeInfo(reflect.Typeof(commonType{})).id)
	checkId(11, mustGetTypeInfo(reflect.Typeof(structType{})).id)
	checkId(12, mustGetTypeInfo(reflect.Typeof(fieldType{})).id)
	builtinIdToType = make(map[typeId]gobType)
	for k, v := range idToType {
		builtinIdToType[k] = v
	}
}

// Array type
type arrayType struct {
	commonType
	Elem typeId
	Len  int
}

func newArrayType(name string, elem gobType, length int) *arrayType {
	a := &arrayType{commonType{name: name}, elem.id(), length}
	setTypeId(a)
	return a
}

func (a *arrayType) safeString(seen map[typeId]bool) string {
	if _, ok := seen[a._id]; ok {
		return a.name
	}
	seen[a._id] = true
	return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen))
}

func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) }

// Slice type
type sliceType struct {
	commonType
	Elem typeId
}

func newSliceType(name string, elem gobType) *sliceType {
	s := &sliceType{commonType{name: name}, elem.id()}
	setTypeId(s)
	return s
}

func (s *sliceType) safeString(seen map[typeId]bool) string {
	if _, ok := seen[s._id]; ok {
		return s.name
	}
	seen[s._id] = true
	return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen))
}

func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) }

// Struct type
type fieldType struct {
	name string
	id   typeId
}

type structType struct {
	commonType
	field []*fieldType
}

func (s *structType) safeString(seen map[typeId]bool) string {
	if s == nil {
		return "<nil>"
	}
	if _, ok := seen[s._id]; ok {
		return s.name
	}
	seen[s._id] = true
	str := s.name + " = struct { "
	for _, f := range s.field {
		str += fmt.Sprintf("%s %s; ", f.name, f.id.gobType().safeString(seen))
	}
	str += "}"
	return str
}

func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) }

func newStructType(name string) *structType {
	s := &structType{commonType{name: name}, nil}
	setTypeId(s)
	return s
}

// Step through the indirections on a type to discover the base type.
// Return the number of indirections.
func indirect(t reflect.Type) (rt reflect.Type, count int) {
	rt = t
	for {
		pt, ok := rt.(*reflect.PtrType)
		if !ok {
			break
		}
		rt = pt.Elem()
		count++
	}
	return
}

func newTypeObject(name string, rt reflect.Type) (gobType, os.Error) {
	switch t := rt.(type) {
	// All basic types are easy: they are predefined.
	case *reflect.BoolType:
		return tBool.gobType(), nil

	case *reflect.IntType, *reflect.Int8Type, *reflect.Int16Type, *reflect.Int32Type, *reflect.Int64Type:
		return tInt.gobType(), nil

	case *reflect.UintType, *reflect.Uint8Type, *reflect.Uint16Type, *reflect.Uint32Type, *reflect.Uint64Type, *reflect.UintptrType:
		return tUint.gobType(), nil

	case *reflect.FloatType, *reflect.Float32Type, *reflect.Float64Type:
		return tFloat.gobType(), nil

	case *reflect.StringType:
		return tString.gobType(), nil

	case *reflect.ArrayType:
		gt, err := getType("", t.Elem())
		if err != nil {
			return nil, err
		}
		return newArrayType(name, gt, t.Len()), nil

	case *reflect.SliceType:
		// []byte == []uint8 is a special case
		if _, ok := t.Elem().(*reflect.Uint8Type); ok {
			return tBytes.gobType(), nil
		}
		gt, err := getType(t.Elem().Name(), t.Elem())
		if err != nil {
			return nil, err
		}
		return newSliceType(name, gt), nil

	case *reflect.StructType:
		// Install the struct type itself before the fields so recursive
		// structures can be constructed safely.
		strType := newStructType(name)
		types[rt] = strType
		idToType[strType.id()] = strType
		field := make([]*fieldType, t.NumField())
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			typ, _ := indirect(f.Type)
			tname := typ.Name()
			if tname == "" {
				tname = f.Type.String()
			}
			gt, err := getType(tname, f.Type)
			if err != nil {
				return nil, err
			}
			field[i] = &fieldType{f.Name, gt.id()}
		}
		strType.field = field
		return strType, nil

	default:
		return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String())
	}
	return nil, nil
}

// getType returns the Gob type describing the given reflect.Type.
// typeLock must be held.
func getType(name string, rt reflect.Type) (gobType, os.Error) {
	// Flatten the data structure by collapsing out pointers
	for {
		pt, ok := rt.(*reflect.PtrType)
		if !ok {
			break
		}
		rt = pt.Elem()
	}
	typ, present := types[rt]
	if present {
		return typ, nil
	}
	typ, err := newTypeObject(name, rt)
	if err == nil {
		types[rt] = typ
	}
	return typ, err
}

func checkId(want, got typeId) {
	if want != got {
		panicln("bootstrap type wrong id:", got.Name(), got, "not", want)
	}
}

// used for building the basic types; called only from init()
func bootstrapType(name string, e interface{}, expect typeId) typeId {
	rt := reflect.Typeof(e)
	_, present := types[rt]
	if present {
		panicln("bootstrap type already present:", name)
	}
	typ := &commonType{name: name}
	types[rt] = typ
	setTypeId(typ)
	checkId(expect, nextId)
	return nextId
}

// Representation of the information we send and receive about this type.
// Each value we send is preceded by its type definition: an encoded int.
// However, the very first time we send the value, we first send the pair
// (-id, wireType).
// For bootstrapping purposes, we assume that the recipient knows how
// to decode a wireType; it is exactly the wireType struct here, interpreted
// using the gob rules for sending a structure, except that we assume the
// ids for wireType and structType are known.  The relevant pieces
// are built in encode.go's init() function.

type wireType struct {
	array *arrayType
	slice *sliceType
	strct *structType
}

func (w *wireType) name() string {
	if w.strct != nil {
		return w.strct.name
	}
	return "unknown"
}

type typeInfo struct {
	id      typeId
	encoder *encEngine
	wire    *wireType
}

var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock

// The reflection type must have all its indirections processed out.
// typeLock must be held.
func getTypeInfo(rt reflect.Type) (*typeInfo, os.Error) {
	if _, ok := rt.(*reflect.PtrType); ok {
		panicln("pointer type in getTypeInfo:", rt.String())
	}
	info, ok := typeInfoMap[rt]
	if !ok {
		info = new(typeInfo)
		name := rt.Name()
		gt, err := getType(name, rt)
		if err != nil {
			return nil, err
		}
		info.id = gt.id()
		t := info.id.gobType()
		switch typ := rt.(type) {
		case *reflect.ArrayType:
			info.wire = &wireType{array: t.(*arrayType)}
		case *reflect.SliceType:
			// []byte == []uint8 is a special case handled separately
			if _, ok := typ.Elem().(*reflect.Uint8Type); !ok {
				info.wire = &wireType{slice: t.(*sliceType)}
			}
		case *reflect.StructType:
			info.wire = &wireType{strct: t.(*structType)}
		}
		typeInfoMap[rt] = info
	}
	return info, nil
}

// Called only when a panic is acceptable and unexpected.
func mustGetTypeInfo(rt reflect.Type) *typeInfo {
	t, err := getTypeInfo(rt)
	if err != nil {
		panicln("getTypeInfo:", err.String())
	}
	return t
}
<commit_msg>to improve the chances for compatibility, open a window of unused ids and specify a lowest id granted to users.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gob

import (
	"fmt"
	"os"
	"reflect"
	"sync"
)

type kind reflect.Type

// Reflection types are themselves interface values holding structs
// describing the type.  Each type has a different struct so that struct can
// be the kind.  For example, if typ is the reflect type for an int8, typ is
// a pointer to a reflect.Int8Type struct; if typ is the reflect type for a
// function, typ is a pointer to a reflect.FuncType struct; we use the type
// of that pointer as the kind.

// typeKind returns a reflect.Type representing typ's kind.  The kind is the
// general kind of type:
//	int8, int16, int, uint, float, func, chan, struct, and so on.
// That is, all struct types have the same kind, all func types have the same
// kind, all int8 types have the same kind, and so on.
func typeKind(typ reflect.Type) kind { return kind(reflect.Typeof(typ)) }

// valueKind returns the kind of the value type
// stored inside the interface v.
func valueKind(v interface{}) reflect.Type { return typeKind(reflect.Typeof(v)) }

// A typeId represents a gob Type as an integer that can be passed on the wire.
// Internally, typeIds are used as keys to a map to recover the underlying type info.
type typeId int32

var nextId typeId       // incremented for each new type we build
var typeLock sync.Mutex // set while building a type
const firstUserId = 64  // lowest id number granted to user

type gobType interface {
	id() typeId
	setId(id typeId)
	Name() string
	string() string // not public; only for debugging
	safeString(seen map[typeId]bool) string
}

var types = make(map[reflect.Type]gobType)
var idToType = make(map[typeId]gobType)
var builtinIdToType map[typeId]gobType // set in init() after builtins are established

func setTypeId(typ gobType) {
	nextId++
	typ.setId(nextId)
	idToType[nextId] = typ
}

func (t typeId) gobType() gobType {
	if t == 0 {
		return nil
	}
	return idToType[t]
}

// string returns the string representation of the type associated with the typeId.
func (t typeId) string() string { return t.gobType().string() }

// Name returns the name of the type associated with the typeId.
func (t typeId) Name() string { return t.gobType().Name() }

// Common elements of all types.
type commonType struct {
	name string
	_id  typeId
}

func (t *commonType) id() typeId { return t._id }

func (t *commonType) setId(id typeId) { t._id = id }

func (t *commonType) string() string { return t.name }

func (t *commonType) safeString(seen map[typeId]bool) string {
	return t.name
}

func (t *commonType) Name() string { return t.name }

// Create and check predefined types
// The string for tBytes is "bytes" not "[]byte" to signify its specialness.

var tBool = bootstrapType("bool", false, 1)
var tInt = bootstrapType("int", int(0), 2)
var tUint = bootstrapType("uint", uint(0), 3)
var tFloat = bootstrapType("float", float64(0), 4)
var tBytes = bootstrapType("bytes", make([]byte, 0), 5)
var tString = bootstrapType("string", "", 6)

// Predefined because it's needed by the Decoder
var tWireType = mustGetTypeInfo(reflect.Typeof(wireType{})).id

func init() {
	// Some magic numbers to make sure there are no surprises.
	checkId(7, tWireType)
	checkId(9, mustGetTypeInfo(reflect.Typeof(commonType{})).id)
	checkId(11, mustGetTypeInfo(reflect.Typeof(structType{})).id)
	checkId(12, mustGetTypeInfo(reflect.Typeof(fieldType{})).id)
	builtinIdToType = make(map[typeId]gobType)
	for k, v := range idToType {
		builtinIdToType[k] = v
	}
	// Move the id space upwards to allow for growth in the predefined world
	// without breaking existing files.
	if nextId > firstUserId {
		panicln("nextId too large:", nextId)
	}
	nextId = firstUserId
}

// Array type
type arrayType struct {
	commonType
	Elem typeId
	Len  int
}

func newArrayType(name string, elem gobType, length int) *arrayType {
	a := &arrayType{commonType{name: name}, elem.id(), length}
	setTypeId(a)
	return a
}

func (a *arrayType) safeString(seen map[typeId]bool) string {
	if _, ok := seen[a._id]; ok {
		return a.name
	}
	seen[a._id] = true
	return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen))
}

func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) }

// Slice type
type sliceType struct {
	commonType
	Elem typeId
}

func newSliceType(name string, elem gobType) *sliceType {
	s := &sliceType{commonType{name: name}, elem.id()}
	setTypeId(s)
	return s
}

func (s *sliceType) safeString(seen map[typeId]bool) string {
	if _, ok := seen[s._id]; ok {
		return s.name
	}
	seen[s._id] = true
	return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen))
}

func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) }

// Struct type
type fieldType struct {
	name string
	id   typeId
}

type structType struct {
	commonType
	field []*fieldType
}

func (s *structType) safeString(seen map[typeId]bool) string {
	if s == nil {
		return "<nil>"
	}
	if _, ok := seen[s._id]; ok {
		return s.name
	}
	seen[s._id] = true
	str := s.name + " = struct { "
	for _, f := range s.field {
		str += fmt.Sprintf("%s %s; ", f.name, f.id.gobType().safeString(seen))
	}
	str += "}"
	return str
}

func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) }

func newStructType(name string) *structType {
	s := &structType{commonType{name: name}, nil}
	setTypeId(s)
	return s
}

// Step through the indirections on a type to discover the base type.
// Return the number of indirections.
func indirect(t reflect.Type) (rt reflect.Type, count int) {
	rt = t
	for {
		pt, ok := rt.(*reflect.PtrType)
		if !ok {
			break
		}
		rt = pt.Elem()
		count++
	}
	return
}

func newTypeObject(name string, rt reflect.Type) (gobType, os.Error) {
	switch t := rt.(type) {
	// All basic types are easy: they are predefined.
	case *reflect.BoolType:
		return tBool.gobType(), nil

	case *reflect.IntType, *reflect.Int8Type, *reflect.Int16Type, *reflect.Int32Type, *reflect.Int64Type:
		return tInt.gobType(), nil

	case *reflect.UintType, *reflect.Uint8Type, *reflect.Uint16Type, *reflect.Uint32Type, *reflect.Uint64Type, *reflect.UintptrType:
		return tUint.gobType(), nil

	case *reflect.FloatType, *reflect.Float32Type, *reflect.Float64Type:
		return tFloat.gobType(), nil

	case *reflect.StringType:
		return tString.gobType(), nil

	case *reflect.ArrayType:
		gt, err := getType("", t.Elem())
		if err != nil {
			return nil, err
		}
		return newArrayType(name, gt, t.Len()), nil

	case *reflect.SliceType:
		// []byte == []uint8 is a special case
		if _, ok := t.Elem().(*reflect.Uint8Type); ok {
			return tBytes.gobType(), nil
		}
		gt, err := getType(t.Elem().Name(), t.Elem())
		if err != nil {
			return nil, err
		}
		return newSliceType(name, gt), nil

	case *reflect.StructType:
		// Install the struct type itself before the fields so recursive
		// structures can be constructed safely.
		strType := newStructType(name)
		types[rt] = strType
		idToType[strType.id()] = strType
		field := make([]*fieldType, t.NumField())
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			typ, _ := indirect(f.Type)
			tname := typ.Name()
			if tname == "" {
				tname = f.Type.String()
			}
			gt, err := getType(tname, f.Type)
			if err != nil {
				return nil, err
			}
			field[i] = &fieldType{f.Name, gt.id()}
		}
		strType.field = field
		return strType, nil

	default:
		return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String())
	}
	return nil, nil
}

// getType returns the Gob type describing the given reflect.Type.
// typeLock must be held.
func getType(name string, rt reflect.Type) (gobType, os.Error) {
	// Flatten the data structure by collapsing out pointers
	for {
		pt, ok := rt.(*reflect.PtrType)
		if !ok {
			break
		}
		rt = pt.Elem()
	}
	typ, present := types[rt]
	if present {
		return typ, nil
	}
	typ, err := newTypeObject(name, rt)
	if err == nil {
		types[rt] = typ
	}
	return typ, err
}

func checkId(want, got typeId) {
	if want != got {
		panicln("bootstrap type wrong id:", got.Name(), got, "not", want)
	}
}

// used for building the basic types; called only from init()
func bootstrapType(name string, e interface{}, expect typeId) typeId {
	rt := reflect.Typeof(e)
	_, present := types[rt]
	if present {
		panicln("bootstrap type already present:", name)
	}
	typ := &commonType{name: name}
	types[rt] = typ
	setTypeId(typ)
	checkId(expect, nextId)
	return nextId
}

// Representation of the information we send and receive about this type.
// Each value we send is preceded by its type definition: an encoded int.
// However, the very first time we send the value, we first send the pair
// (-id, wireType).
// For bootstrapping purposes, we assume that the recipient knows how
// to decode a wireType; it is exactly the wireType struct here, interpreted
// using the gob rules for sending a structure, except that we assume the
// ids for wireType and structType are known.  The relevant pieces
// are built in encode.go's init() function.

type wireType struct {
	array *arrayType
	slice *sliceType
	strct *structType
}

func (w *wireType) name() string {
	if w.strct != nil {
		return w.strct.name
	}
	return "unknown"
}

type typeInfo struct {
	id      typeId
	encoder *encEngine
	wire    *wireType
}

var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock

// The reflection type must have all its indirections processed out.
// typeLock must be held.
func getTypeInfo(rt reflect.Type) (*typeInfo, os.Error) {
	if _, ok := rt.(*reflect.PtrType); ok {
		panicln("pointer type in getTypeInfo:", rt.String())
	}
	info, ok := typeInfoMap[rt]
	if !ok {
		info = new(typeInfo)
		name := rt.Name()
		gt, err := getType(name, rt)
		if err != nil {
			return nil, err
		}
		info.id = gt.id()
		t := info.id.gobType()
		switch typ := rt.(type) {
		case *reflect.ArrayType:
			info.wire = &wireType{array: t.(*arrayType)}
		case *reflect.SliceType:
			// []byte == []uint8 is a special case handled separately
			if _, ok := typ.Elem().(*reflect.Uint8Type); !ok {
				info.wire = &wireType{slice: t.(*sliceType)}
			}
		case *reflect.StructType:
			info.wire = &wireType{strct: t.(*structType)}
		}
		typeInfoMap[rt] = info
	}
	return info, nil
}

// Called only when a panic is acceptable and unexpected.
func mustGetTypeInfo(rt reflect.Type) *typeInfo {
	t, err := getTypeInfo(rt)
	if err != nil {
		panicln("getTypeInfo:", err.String())
	}
	return t
}
<|endoftext|>


<commit_before>package xmmsclient

import (
	"bytes"
	"encoding/binary"
)

func serializeInt(i XmmsInt, buffer *bytes.Buffer) error {
	return binary.Write(buffer, binary.BigEndian, i)
}

func serializeString(s XmmsString, buffer *bytes.Buffer) error {
	err := binary.Write(buffer, binary.BigEndian, uint32(len(s)+1))
	if err != nil {
		return err
	}

	err = binary.Write(buffer, binary.BigEndian, []byte(s))
	if err != nil {
		return err
	}

	err = binary.Write(buffer, binary.BigEndian, byte(0))
	if err != nil {
		return err
	}

	return nil
}

func serializeList(l XmmsList, buffer *bytes.Buffer) error {
	err := binary.Write(buffer, binary.BigEndian, TypeNone)
	if err != nil {
		return err
	}

	err = binary.Write(buffer, binary.BigEndian, uint32(len(l)))
	if err != nil {
		return err
	}

	for _, entry := range l {
		err = serializeXmmsValue(entry, buffer)
		if err != nil {
			return err
		}
	}

	return nil
}

func serializeDict(dict XmmsDict, buffer *bytes.Buffer) error {
	err := binary.Write(buffer, binary.BigEndian, uint32(len(dict)))
	if err != nil {
		return err
	}

	for k, v := range dict {
		err = serializeString(XmmsString(k), buffer)
		if err != nil {
			return err
		}

		err = serializeXmmsValue(v, buffer)
		if err != nil {
			return err
		}
	}

	return nil
}

func serializeXmmsValue(value XmmsValue, buffer *bytes.Buffer) (err error) {
	switch value.(type) {
	case XmmsInt:
		err = binary.Write(buffer, binary.BigEndian, TypeInt64)
		if err == nil {
			serializeInt(value.(XmmsInt), buffer)
		}
	case XmmsString:
		err = binary.Write(buffer, binary.BigEndian, TypeString)
		if err == nil {
			serializeString(value.(XmmsString), buffer)
		}
	case XmmsDict:
		err = binary.Write(buffer, binary.BigEndian, TypeDict)
		if err == nil {
			serializeDict(value.(XmmsDict), buffer)
		}
	case XmmsList:
		err = binary.Write(buffer, binary.BigEndian, TypeList)
		if err == nil {
			serializeList(value.(XmmsList), buffer)
		}
	}
	return nil
}
<commit_msg>Add support for serializing errors.<commit_after>package xmmsclient

import (
	"bytes"
	"encoding/binary"
)

func serializeInt(i XmmsInt, buffer *bytes.Buffer) error {
	return binary.Write(buffer, binary.BigEndian, i)
}

func serializeString(s []byte, buffer *bytes.Buffer) error {
	err := binary.Write(buffer, binary.BigEndian, uint32(len(s)+1))
	if err != nil {
		return err
	}

	err = binary.Write(buffer, binary.BigEndian, s)
	if err != nil {
		return err
	}

	err = binary.Write(buffer, binary.BigEndian, byte(0))
	if err != nil {
		return err
	}

	return nil
}

func serializeList(l XmmsList, buffer *bytes.Buffer) error {
	err := binary.Write(buffer, binary.BigEndian, TypeNone)
	if err != nil {
		return err
	}

	err = binary.Write(buffer, binary.BigEndian, uint32(len(l)))
	if err != nil {
		return err
	}

	for _, entry := range l {
		err = serializeXmmsValue(entry, buffer)
		if err != nil {
			return err
		}
	}

	return nil
}

func serializeDict(dict XmmsDict, buffer *bytes.Buffer) error {
	err := binary.Write(buffer, binary.BigEndian, uint32(len(dict)))
	if err != nil {
		return err
	}

	for k, v := range dict {
		err = serializeString([]byte(k), buffer)
		if err != nil {
			return err
		}

		err = serializeXmmsValue(v, buffer)
		if err != nil {
			return err
		}
	}

	return nil
}

func serializeXmmsValue(value XmmsValue, buffer *bytes.Buffer) (err error) {
	switch value.(type) {
	case XmmsInt:
		err = binary.Write(buffer, binary.BigEndian, TypeInt64)
		if err == nil {
			serializeInt(value.(XmmsInt), buffer)
		}
	case XmmsString:
		err = binary.Write(buffer, binary.BigEndian, TypeString)
		if err == nil {
			serializeString([]byte(value.(XmmsString)), buffer)
		}
	case XmmsError:
		err = binary.Write(buffer, binary.BigEndian, TypeError)
		if err == nil {
			serializeString([]byte(value.(XmmsError)), buffer)
		}
	case XmmsDict:
		err = binary.Write(buffer, binary.BigEndian, TypeDict)
		if err == nil {
			serializeDict(value.(XmmsDict), buffer)
		}
	case XmmsList:
		err = binary.Write(buffer, binary.BigEndian, TypeList)
		if err == nil {
			serializeList(value.(XmmsList), buffer)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2015 Cesanta Software Ltd.

   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

       https://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 authn

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/go-ldap/ldap"
	"github.com/cesanta/glog"
)

type LabelMap struct {
	Attribute string `yaml:"attribute,omitempty"`
	ParseCN   bool   `yaml:"parse_cn,omitempty"`
}

type LDAPAuthConfig struct {
	Addr                  string              `yaml:"addr,omitempty"`
	TLS                   string              `yaml:"tls,omitempty"`
	InsecureTLSSkipVerify bool                `yaml:"insecure_tls_skip_verify,omitempty"`
	CACertificate         string              `yaml:"ca_certificate,omitempty"`
	Base                  string              `yaml:"base,omitempty"`
	Filter                string              `yaml:"filter,omitempty"`
	BindDN                string              `yaml:"bind_dn,omitempty"`
	BindPasswordFile      string              `yaml:"bind_password_file,omitempty"`
	LabelMaps             map[string]LabelMap `yaml:"labels,omitempty"`
	GroupBaseDN           string              `yaml:"group_base_dn,omitempty"`
	GroupFilter           string              `yaml:"group_filter,omitempty"`
}

type LDAPAuth struct {
	config *LDAPAuthConfig
}

func NewLDAPAuth(c *LDAPAuthConfig) (*LDAPAuth, error) {
	if c.TLS == "" && strings.HasSuffix(c.Addr, ":636") {
		c.TLS = "always"
	}
	return &LDAPAuth{
		config: c,
	}, nil
}

//How to authenticate user, please refer to https://github.com/go-ldap/ldap/blob/master/example_test.go#L166
func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, Labels, error) {
	if account == "" || password == "" {
		return false, nil, NoMatch
	}
	l, err := la.ldapConnection()
	if err != nil {
		return false, nil, err
	}
	defer l.Close()

	// First bind with a read only user, to prevent the following search won't perform any write action
	if bindErr := la.bindReadOnlyUser(l); bindErr != nil {
		return false, nil, bindErr
	}

	account = la.escapeAccountInput(account)

	filter := la.getFilter(account)

	labelAttributes, labelsConfigErr := la.getLabelAttributes()
	if labelsConfigErr != nil {
		return false, nil, labelsConfigErr
	}

	accountEntryDN, entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &labelAttributes)
	if uSearchErr != nil {
		return false, nil, uSearchErr
	}
	if accountEntryDN == "" {
		return false, nil, NoMatch // User does not exist
	}

	// Bind as the user to verify their password
	if len(accountEntryDN) > 0 {
		err := l.Bind(accountEntryDN, string(password))
		if err != nil {
			if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) {
				return false, nil, nil
			}
			return false, nil, err
		}
	}
	// Rebind as the read only user for any futher queries
	if bindErr := la.bindReadOnlyUser(l); bindErr != nil {
		return false, nil, bindErr
	}

	// Extract labels from the attribute values
	labels, labelsExtractErr := la.getLabelsFromMap(entryAttrMap)
	if labelsExtractErr != nil {
		return false, nil, labelsExtractErr
	}

	return true, labels, nil
}

func (la *LDAPAuth) bindReadOnlyUser(l *ldap.Conn) error {
	if la.config.BindDN != "" {
		password, err := ioutil.ReadFile(la.config.BindPasswordFile)
		if err != nil {
			return err
		}
		password_str := strings.TrimSpace(string(password))
		glog.V(2).Infof("Bind read-only user (DN = %s)", la.config.BindDN)
		err = l.Bind(la.config.BindDN, password_str)
		if err != nil {
			return err
		}
	}
	return nil
}

//To prevent LDAP injection, some characters must be escaped for searching
//e.g. char '\' will be replaced by hex '\5c'
//Filter meta chars are choosen based on filter complier code
//https://github.com/go-ldap/ldap/blob/master/filter.go#L159
func (la *LDAPAuth) escapeAccountInput(account string) string {
	r := strings.NewReplacer(
		`\`, `\5c`,
		`(`, `\28`,
		`)`, `\29`,
		`!`, `\21`,
		`*`, `\2a`,
		`&`, `\26`,
		`|`, `\7c`,
		`=`, `\3d`,
		`>`, `\3e`,
		`<`, `\3c`,
		`~`, `\7e`,
	)
	return r.Replace(account)
}

func (la *LDAPAuth) ldapConnection() (*ldap.Conn, error) {
	var l *ldap.Conn
	var err error

	tlsConfig := &tls.Config{InsecureSkipVerify: true}
	if !la.config.InsecureTLSSkipVerify {
		addr := strings.Split(la.config.Addr, ":")
		if la.config.CACertificate != "" {
			pool := x509.NewCertPool()
			pem, err := ioutil.ReadFile(la.config.CACertificate)
			if err != nil {
				return nil, fmt.Errorf("Error loading CA File: %s", err)
			}
			ok := pool.AppendCertsFromPEM(pem)
			if !ok {
				return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", la.config.CACertificate)
			}
			tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0], RootCAs: pool}
		} else {
			tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]}
		}
	}

	if la.config.TLS == "" || la.config.TLS == "none" || la.config.TLS == "starttls" {
		glog.V(2).Infof("Dial: starting...%s", la.config.Addr)
		l, err = ldap.Dial("tcp", fmt.Sprintf("%s", la.config.Addr))
		if err == nil && la.config.TLS == "starttls" {
			glog.V(2).Infof("StartTLS...")
			if tlserr := l.StartTLS(tlsConfig); tlserr != nil {
				return nil, tlserr
			}
		}
	} else if la.config.TLS == "always" {
		glog.V(2).Infof("DialTLS: starting...%s", la.config.Addr)
		l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s", la.config.Addr), tlsConfig)
	}
	if err != nil {
		return nil, err
	}
	return l, nil
}

func (la *LDAPAuth) getFilter(account string) string {
	filter := strings.NewReplacer("${account}", account).Replace(la.config.Filter)
	glog.V(2).Infof("search filter is %s", filter)
	return filter
}

//ldap search and return required attributes' value from searched entries
//default return entry's DN value if you leave attrs array empty
func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, map[string][]string, error) {
	if l == nil {
		return "", nil, fmt.Errorf("No ldap connection!")
	}
	glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter)
	searchRequest := ldap.NewSearchRequest(
		*baseDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		*filter,
		*attrs,
		nil)
	sr, err := l.Search(searchRequest)
	if err != nil {
		return "", nil, err
	}

	if len(sr.Entries) == 0 {
		return "", nil, nil // User does not exist
	} else if len(sr.Entries) > 1 {
		return "", nil, fmt.Errorf("Too many entries returned.")
	}

	attributes := make(map[string][]string)
	var entryDn string
	for _, entry := range sr.Entries {
		entryDn = entry.DN
		if len(*attrs) == 0 {
			glog.V(2).Infof("Entry DN = %s", entryDn)
		} else {
			for _, attr := range *attrs {
				values := entry.GetAttributeValues(attr)
				glog.V(2).Infof("Entry %s = %s", attr, strings.Join(values, "\n"))
				attributes[attr] = values
			}
		}
	}

	return entryDn, attributes, nil
}

func (la *LDAPAuth) getLabelAttributes() ([]string, error) {
	labelAttributes := make([]string, len(la.config.LabelMaps))
	i := 0
	for key, mapping := range la.config.LabelMaps {
		if mapping.Attribute == "" {
			return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key)
		}
		labelAttributes[i] = mapping.Attribute
		i++
	}
	return labelAttributes, nil
}

func (la *LDAPAuth) getLabelsFromMap(attrMap map[string][]string) (map[string][]string, error) {
	labels := make(map[string][]string)
	for key, mapping := range la.config.LabelMaps {
		if mapping.Attribute == "" {
			return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key)
		}

		mappingValues := attrMap[mapping.Attribute]
		if mappingValues != nil {
			if mapping.ParseCN {
				// shorten attribute to its common name
				for i, value := range mappingValues {
					cn := la.getCNFromDN(value)
					mappingValues[i] = cn
				}
			}
			labels[key] = mappingValues
		}
	}
	return labels, nil
}

func (la *LDAPAuth) getCNFromDN(dn string) string {
	parsedDN, err := ldap.ParseDN(dn)
	if err != nil || len(parsedDN.RDNs) > 0 {
		for _, rdn := range parsedDN.RDNs {
			for _, rdnAttr := range rdn.Attributes {
				if rdnAttr.Type == "CN" {
					return rdnAttr.Value
				}
			}
		}
	}

	// else try using raw DN
	return dn
}

func (la *LDAPAuth) Stop() {
}

func (la *LDAPAuth) Name() string {
	return "LDAP"
}
<commit_msg>Remove unused configuration fields, never implemented?<commit_after>/*
   Copyright 2015 Cesanta Software Ltd.

   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

       https://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 authn

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/go-ldap/ldap"
	"github.com/cesanta/glog"
)

type LabelMap struct {
	Attribute string `yaml:"attribute,omitempty"`
	ParseCN   bool   `yaml:"parse_cn,omitempty"`
}

type LDAPAuthConfig struct {
	Addr                  string              `yaml:"addr,omitempty"`
	TLS                   string              `yaml:"tls,omitempty"`
	InsecureTLSSkipVerify bool                `yaml:"insecure_tls_skip_verify,omitempty"`
	CACertificate         string              `yaml:"ca_certificate,omitempty"`
	Base                  string              `yaml:"base,omitempty"`
	Filter                string              `yaml:"filter,omitempty"`
	BindDN                string              `yaml:"bind_dn,omitempty"`
	BindPasswordFile      string              `yaml:"bind_password_file,omitempty"`
	LabelMaps             map[string]LabelMap `yaml:"labels,omitempty"`
}

type LDAPAuth struct {
	config *LDAPAuthConfig
}

func NewLDAPAuth(c *LDAPAuthConfig) (*LDAPAuth, error) {
	if c.TLS == "" && strings.HasSuffix(c.Addr, ":636") {
		c.TLS = "always"
	}
	return &LDAPAuth{
		config: c,
	}, nil
}

//How to authenticate user, please refer to https://github.com/go-ldap/ldap/blob/master/example_test.go#L166
func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, Labels, error) {
	if account == "" || password == "" {
		return false, nil, NoMatch
	}
	l, err := la.ldapConnection()
	if err != nil {
		return false, nil, err
	}
	defer l.Close()

	// First bind with a read only user, to prevent the following search won't perform any write action
	if bindErr := la.bindReadOnlyUser(l); bindErr != nil {
		return false, nil, bindErr
	}

	account = la.escapeAccountInput(account)

	filter := la.getFilter(account)

	labelAttributes, labelsConfigErr := la.getLabelAttributes()
	if labelsConfigErr != nil {
		return false, nil, labelsConfigErr
	}

	accountEntryDN, entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &labelAttributes)
	if uSearchErr != nil {
		return false, nil, uSearchErr
	}
	if accountEntryDN == "" {
		return false, nil, NoMatch // User does not exist
	}

	// Bind as the user to verify their password
	if len(accountEntryDN) > 0 {
		err := l.Bind(accountEntryDN, string(password))
		if err != nil {
			if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) {
				return false, nil, nil
			}
			return false, nil, err
		}
	}
	// Rebind as the read only user for any futher queries
	if bindErr := la.bindReadOnlyUser(l); bindErr != nil {
		return false, nil, bindErr
	}

	// Extract labels from the attribute values
	labels, labelsExtractErr := la.getLabelsFromMap(entryAttrMap)
	if labelsExtractErr != nil {
		return false, nil, labelsExtractErr
	}

	return true, labels, nil
}

func (la *LDAPAuth) bindReadOnlyUser(l *ldap.Conn) error {
	if la.config.BindDN != "" {
		password, err := ioutil.ReadFile(la.config.BindPasswordFile)
		if err != nil {
			return err
		}
		password_str := strings.TrimSpace(string(password))
		glog.V(2).Infof("Bind read-only user (DN = %s)", la.config.BindDN)
		err = l.Bind(la.config.BindDN, password_str)
		if err != nil {
			return err
		}
	}
	return nil
}

//To prevent LDAP injection, some characters must be escaped for searching
//e.g. char '\' will be replaced by hex '\5c'
//Filter meta chars are choosen based on filter complier code
//https://github.com/go-ldap/ldap/blob/master/filter.go#L159
func (la *LDAPAuth) escapeAccountInput(account string) string {
	r := strings.NewReplacer(
		`\`, `\5c`,
		`(`, `\28`,
		`)`, `\29`,
		`!`, `\21`,
		`*`, `\2a`,
		`&`, `\26`,
		`|`, `\7c`,
		`=`, `\3d`,
		`>`, `\3e`,
		`<`, `\3c`,
		`~`, `\7e`,
	)
	return r.Replace(account)
}

func (la *LDAPAuth) ldapConnection() (*ldap.Conn, error) {
	var l *ldap.Conn
	var err error

	tlsConfig := &tls.Config{InsecureSkipVerify: true}
	if !la.config.InsecureTLSSkipVerify {
		addr := strings.Split(la.config.Addr, ":")
		if la.config.CACertificate != "" {
			pool := x509.NewCertPool()
			pem, err := ioutil.ReadFile(la.config.CACertificate)
			if err != nil {
				return nil, fmt.Errorf("Error loading CA File: %s", err)
			}
			ok := pool.AppendCertsFromPEM(pem)
			if !ok {
				return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", la.config.CACertificate)
			}
			tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0], RootCAs: pool}
		} else {
			tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]}
		}
	}

	if la.config.TLS == "" || la.config.TLS == "none" || la.config.TLS == "starttls" {
		glog.V(2).Infof("Dial: starting...%s", la.config.Addr)
		l, err = ldap.Dial("tcp", fmt.Sprintf("%s", la.config.Addr))
		if err == nil && la.config.TLS == "starttls" {
			glog.V(2).Infof("StartTLS...")
			if tlserr := l.StartTLS(tlsConfig); tlserr != nil {
				return nil, tlserr
			}
		}
	} else if la.config.TLS == "always" {
		glog.V(2).Infof("DialTLS: starting...%s", la.config.Addr)
		l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s", la.config.Addr), tlsConfig)
	}
	if err != nil {
		return nil, err
	}
	return l, nil
}

func (la *LDAPAuth) getFilter(account string) string {
	filter := strings.NewReplacer("${account}", account).Replace(la.config.Filter)
	glog.V(2).Infof("search filter is %s", filter)
	return filter
}

//ldap search and return required attributes' value from searched entries
//default return entry's DN value if you leave attrs array empty
func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, map[string][]string, error) {
	if l == nil {
		return "", nil, fmt.Errorf("No ldap connection!")
	}
	glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter)
	searchRequest := ldap.NewSearchRequest(
		*baseDN,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		*filter,
		*attrs,
		nil)
	sr, err := l.Search(searchRequest)
	if err != nil {
		return "", nil, err
	}

	if len(sr.Entries) == 0 {
		return "", nil, nil // User does not exist
	} else if len(sr.Entries) > 1 {
		return "", nil, fmt.Errorf("Too many entries returned.")
	}

	attributes := make(map[string][]string)
	var entryDn string
	for _, entry := range sr.Entries {
		entryDn = entry.DN
		if len(*attrs) == 0 {
			glog.V(2).Infof("Entry DN = %s", entryDn)
		} else {
			for _, attr := range *attrs {
				values := entry.GetAttributeValues(attr)
				glog.V(2).Infof("Entry %s = %s", attr, strings.Join(values, "\n"))
				attributes[attr] = values
			}
		}
	}

	return entryDn, attributes, nil
}

func (la *LDAPAuth) getLabelAttributes() ([]string, error) {
	labelAttributes := make([]string, len(la.config.LabelMaps))
	i := 0
	for key, mapping := range la.config.LabelMaps {
		if mapping.Attribute == "" {
			return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key)
		}
		labelAttributes[i] = mapping.Attribute
		i++
	}
	return labelAttributes, nil
}

func (la *LDAPAuth) getLabelsFromMap(attrMap map[string][]string) (map[string][]string, error) {
	labels := make(map[string][]string)
	for key, mapping := range la.config.LabelMaps {
		if mapping.Attribute == "" {
			return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key)
		}

		mappingValues := attrMap[mapping.Attribute]
		if mappingValues != nil {
			if mapping.ParseCN {
				// shorten attribute to its common name
				for i, value := range mappingValues {
					cn := la.getCNFromDN(value)
					mappingValues[i] = cn
				}
			}
			labels[key] = mappingValues
		}
	}
	return labels, nil
}

func (la *LDAPAuth) getCNFromDN(dn string) string {
	parsedDN, err := ldap.ParseDN(dn)
	if err != nil || len(parsedDN.RDNs) > 0 {
		for _, rdn := range parsedDN.RDNs {
			for _, rdnAttr := range rdn.Attributes {
				if rdnAttr.Type == "CN" {
					return rdnAttr.Value
				}
			}
		}
	}

	// else try using raw DN
	return dn
}

func (la *LDAPAuth) Stop() {
}

func (la *LDAPAuth) Name() string {
	return "LDAP"
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package testing

import (
	"io/ioutil"
	"os"
	"path/filepath"

	. "launchpad.net/gocheck"

	"launchpad.net/juju-core/environs/config"
)

// EnvironConfig returns a default environment configuration suitable for
// testing.
func EnvironConfig(c *C) *config.Config {
	cfg, err := config.New(map[string]interface{}{
		"type":            "test",
		"name":            "test-name",
		"default-series":  "test-series",
		"authorized-keys": "test-keys",
		"agent-version":   "9.9.9.9",
		"ca-cert":         CACert,
		"ca-private-key":  "",
	})
	c.Assert(err, IsNil)
	return cfg
}

const SampleEnvName = "erewhemos"
const EnvDefault = "default:\n  " + SampleEnvName + "\n"

// Environment names below are explicit as it makes them more readable.
const SingleEnvConfigNoDefault = `
environments:
    erewhemos:
        type: dummy
        state-server: true
        authorized-keys: i-am-a-key
        admin-secret: conn-from-name-secret
`

const SingleEnvConfig = EnvDefault + SingleEnvConfigNoDefault

const MultipleEnvConfigNoDefault = `
environments:
    erewhemos:
        type: dummy
        state-server: true
        authorized-keys: i-am-a-key
        admin-secret: conn-from-name-secret
    erewhemos-2:
        type: dummy
        state-server: true
        authorized-keys: i-am-a-key
        admin-secret: conn-from-name-secret
`

const MultipleEnvConfig = EnvDefault + MultipleEnvConfigNoDefault

const SampleCertName = "erewhemos"

type TestFile struct {
	Name, Data string
}

type FakeHome struct {
	oldHomeEnv     string
	oldJujuEnv     string
	oldJujuHomeEnv string
	oldJujuHome    string
	files          []TestFile
}

// MakeFakeHomeNoEnvironments creates a new temporary directory through the
// test checker, and overrides the HOME environment variable to point to this
// new temporary directory.
//
// No ~/.juju/environments.yaml exists, but CAKeys are written for each of the
// 'certNames' specified, and the id_rsa.pub file is written to to the .ssh
// dir.
func MakeFakeHomeNoEnvironments(c *C, certNames ...string) *FakeHome {
	fake := MakeEmptyFakeHome(c)

	for _, name := range certNames {
		err := ioutil.WriteFile(config.JujuHomePath(name+"-cert.pem"), []byte(CACert), 0600)
		c.Assert(err, IsNil)
		err = ioutil.WriteFile(config.JujuHomePath(name+"-private-key.pem"), []byte(CAKey), 0600)
		c.Assert(err, IsNil)
	}

	err := os.Mkdir(HomePath(".ssh"), 0777)
	c.Assert(err, IsNil)
	err = ioutil.WriteFile(HomePath(".ssh", "id_rsa.pub"), []byte("auth key\n"), 0666)
	c.Assert(err, IsNil)

	return fake
}

// MakeFakeHome creates a new temporary directory through the test checker,
// and overrides the HOME environment variable to point to this new temporary
// directory.
//
// A new ~/.juju/environments.yaml file is created with the content of the
// `envConfig` parameter, and CAKeys are written for each of the 'certNames'
// specified.
func MakeFakeHome(c *C, envConfig string, certNames ...string) *FakeHome {
	fake := MakeFakeHomeNoEnvironments(c, certNames...)

	envs := config.JujuHomePath("environments.yaml")
	err := ioutil.WriteFile(envs, []byte(envConfig), 0644)
	c.Assert(err, IsNil)

	return fake
}

func MakeEmptyFakeHome(c *C) *FakeHome {
	fake := MakeEmptyFakeHomeWithoutJuju(c)
	err := os.Mkdir(config.JujuHome(), 0700)
	c.Assert(err, IsNil)
	return fake
}

func MakeEmptyFakeHomeWithoutJuju(c *C) *FakeHome {
	oldHomeEnv := os.Getenv("HOME")
	oldJujuHomeEnv := os.Getenv("JUJU_HOME")
	oldJujuEnv := os.Getenv("JUJU_ENV")
	fakeHome := c.MkDir()
	os.Setenv("HOME", fakeHome)
	os.Setenv("JUJU_HOME", "")
	os.Setenv("JUJU_ENV", "")
	jujuHome := filepath.Join(fakeHome, ".juju")
	oldJujuHome := config.SetJujuHome(jujuHome)
	return &FakeHome{
		oldHomeEnv:     oldHomeEnv,
		oldJujuEnv:     oldJujuEnv,
		oldJujuHomeEnv: oldJujuHomeEnv,
		oldJujuHome:    oldJujuHome,
		files:          []TestFile{},
	}
}

func HomePath(names ...string) string {
	all := append([]string{os.Getenv("HOME")}, names...)
	return filepath.Join(all...)
}

func (h *FakeHome) Restore() {
	config.SetJujuHome(h.oldJujuHome)
	os.Setenv("JUJU_ENV", h.oldJujuEnv)
	os.Setenv("JUJU_HOME", h.oldJujuHomeEnv)
	os.Setenv("HOME", h.oldHomeEnv)
}

func (h *FakeHome) AddFiles(c *C, files []TestFile) {
	for _, f := range files {
		path := HomePath(f.Name)
		err := os.MkdirAll(filepath.Dir(path), 0700)
		c.Assert(err, IsNil)
		err = ioutil.WriteFile(path, []byte(f.Data), 0666)
		c.Assert(err, IsNil)
		h.files = append(h.files, f)
	}
}

// FileContents returns the test file contents for the
// given specified path (which may be relative, so
// we compare with the base filename only).
func (h *FakeHome) FileContents(c *C, path string) string {
	for _, f := range h.files {
		if filepath.Base(f.Name) == filepath.Base(path) {
			return f.Data
		}
	}
	c.Fatalf("path attribute holds unknown test file: %q", path)
	panic("unreachable")
}

// FileExists returns if the given relative file path exists
// in the fake home.
func (h *FakeHome) FileExists(path string) bool {
	for _, f := range h.files {
		if f.Name == path {
			return true
		}
	}
	return false
}

func MakeFakeHomeWithFiles(c *C, files []TestFile) *FakeHome {
	fake := MakeEmptyFakeHome(c)
	fake.AddFiles(c, files)
	return fake
}

func MakeSampleHome(c *C) *FakeHome {
	return MakeFakeHome(c, SingleEnvConfig, SampleCertName)
}

func MakeMultipleEnvHome(c *C) *FakeHome {
	return MakeFakeHome(c, MultipleEnvConfig, SampleCertName, "erewhemos-2")
}

// PatchEnvironment provides a test a simple way to override a single
// environment variable. A function is returned that will return the
// environment to what it was before.
func PatchEnvironment(name, value string) func() {
	oldValue := os.Getenv(name)
	os.Setenv(name, value)
	return func() { os.Setenv(name, oldValue) }
}
<commit_msg>Update fake home for logging config.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package testing

import (
	"io/ioutil"
	"os"
	"path/filepath"

	. "launchpad.net/gocheck"

	"launchpad.net/juju-core/environs/config"
)

// EnvironConfig returns a default environment configuration suitable for
// testing.
func EnvironConfig(c *C) *config.Config {
	cfg, err := config.New(map[string]interface{}{
		"type":            "test",
		"name":            "test-name",
		"default-series":  "test-series",
		"authorized-keys": "test-keys",
		"agent-version":   "9.9.9.9",
		"ca-cert":         CACert,
		"ca-private-key":  "",
	})
	c.Assert(err, IsNil)
	return cfg
}

const SampleEnvName = "erewhemos"
const EnvDefault = "default:\n  " + SampleEnvName + "\n"

// Environment names below are explicit as it makes them more readable.
const SingleEnvConfigNoDefault = `
environments:
    erewhemos:
        type: dummy
        state-server: true
        authorized-keys: i-am-a-key
        admin-secret: conn-from-name-secret
`

const SingleEnvConfig = EnvDefault + SingleEnvConfigNoDefault

const MultipleEnvConfigNoDefault = `
environments:
    erewhemos:
        type: dummy
        state-server: true
        authorized-keys: i-am-a-key
        admin-secret: conn-from-name-secret
    erewhemos-2:
        type: dummy
        state-server: true
        authorized-keys: i-am-a-key
        admin-secret: conn-from-name-secret
`

const MultipleEnvConfig = EnvDefault + MultipleEnvConfigNoDefault

const SampleCertName = "erewhemos"

type TestFile struct {
	Name, Data string
}

type FakeHome struct {
	oldEnvironment map[string]string
	oldJujuHome    string
	files          []TestFile
}

// MakeFakeHomeNoEnvironments creates a new temporary directory through the
// test checker, and overrides the HOME environment variable to point to this
// new temporary directory.
//
// No ~/.juju/environments.yaml exists, but CAKeys are written for each of the
// 'certNames' specified, and the id_rsa.pub file is written to to the .ssh
// dir.
func MakeFakeHomeNoEnvironments(c *C, certNames ...string) *FakeHome {
	fake := MakeEmptyFakeHome(c)

	for _, name := range certNames {
		err := ioutil.WriteFile(config.JujuHomePath(name+"-cert.pem"), []byte(CACert), 0600)
		c.Assert(err, IsNil)
		err = ioutil.WriteFile(config.JujuHomePath(name+"-private-key.pem"), []byte(CAKey), 0600)
		c.Assert(err, IsNil)
	}

	err := os.Mkdir(HomePath(".ssh"), 0777)
	c.Assert(err, IsNil)
	err = ioutil.WriteFile(HomePath(".ssh", "id_rsa.pub"), []byte("auth key\n"), 0666)
	c.Assert(err, IsNil)

	return fake
}

// MakeFakeHome creates a new temporary directory through the test checker,
// and overrides the HOME environment variable to point to this new temporary
// directory.
//
// A new ~/.juju/environments.yaml file is created with the content of the
// `envConfig` parameter, and CAKeys are written for each of the 'certNames'
// specified.
func MakeFakeHome(c *C, envConfig string, certNames ...string) *FakeHome {
	fake := MakeFakeHomeNoEnvironments(c, certNames...)

	envs := config.JujuHomePath("environments.yaml")
	err := ioutil.WriteFile(envs, []byte(envConfig), 0644)
	c.Assert(err, IsNil)

	return fake
}

func MakeEmptyFakeHome(c *C) *FakeHome {
	fake := MakeEmptyFakeHomeWithoutJuju(c)
	err := os.Mkdir(config.JujuHome(), 0700)
	c.Assert(err, IsNil)
	return fake
}

func MakeEmptyFakeHomeWithoutJuju(c *C) *FakeHome {
	oldEnvironment := make(map[string]string)
	for _, name := range []string{"HOME", "JUJU_HOME", "JUJU_ENV", "JUJU_LOGGING_CONFIG"} {
		oldEnvironment[name] = os.Getenv(name)
	}
	fakeHome := c.MkDir()
	os.Setenv("HOME", fakeHome)
	os.Setenv("JUJU_HOME", "")
	os.Setenv("JUJU_ENV", "")
	os.Setenv("JUJU_LOGGING_CONFIG", "")
	jujuHome := filepath.Join(fakeHome, ".juju")
	oldJujuHome := config.SetJujuHome(jujuHome)
	return &FakeHome{
		oldEnvironment: oldEnvironment,
		oldJujuHome:    oldJujuHome,
		files:          []TestFile{},
	}
}

func HomePath(names ...string) string {
	all := append([]string{os.Getenv("HOME")}, names...)
	return filepath.Join(all...)
}

func (h *FakeHome) Restore() {
	config.SetJujuHome(h.oldJujuHome)
	for name, value := range h.oldEnvironment {
		os.Setenv(name, value)
	}
}

func (h *FakeHome) AddFiles(c *C, files []TestFile) {
	for _, f := range files {
		path := HomePath(f.Name)
		err := os.MkdirAll(filepath.Dir(path), 0700)
		c.Assert(err, IsNil)
		err = ioutil.WriteFile(path, []byte(f.Data), 0666)
		c.Assert(err, IsNil)
		h.files = append(h.files, f)
	}
}

// FileContents returns the test file contents for the
// given specified path (which may be relative, so
// we compare with the base filename only).
func (h *FakeHome) FileContents(c *C, path string) string {
	for _, f := range h.files {
		if filepath.Base(f.Name) == filepath.Base(path) {
			return f.Data
		}
	}
	c.Fatalf("path attribute holds unknown test file: %q", path)
	panic("unreachable")
}

// FileExists returns if the given relative file path exists
// in the fake home.
func (h *FakeHome) FileExists(path string) bool {
	for _, f := range h.files {
		if f.Name == path {
			return true
		}
	}
	return false
}

func MakeFakeHomeWithFiles(c *C, files []TestFile) *FakeHome {
	fake := MakeEmptyFakeHome(c)
	fake.AddFiles(c, files)
	return fake
}

func MakeSampleHome(c *C) *FakeHome {
	return MakeFakeHome(c, SingleEnvConfig, SampleCertName)
}

func MakeMultipleEnvHome(c *C) *FakeHome {
	return MakeFakeHome(c, MultipleEnvConfig, SampleCertName, "erewhemos-2")
}

// PatchEnvironment provides a test a simple way to override a single
// environment variable. A function is returned that will return the
// environment to what it was before.
func PatchEnvironment(name, value string) func() {
	oldValue := os.Getenv(name)
	os.Setenv(name, value)
	return func() { os.Setenv(name, oldValue) }
}
<|endoftext|>