<commit_before>package main

import (
	"crypto/rsa"
	"io/ioutil"
	"os"
	"testing"
	"time"

	"code.google.com/p/go.crypto/ssh"
	. "github.com/smartystreets/goconvey/convey"
	// "io/ioutil"
	// "fmt"
)

type TestWriter struct {
	Written []byte
}

func (t *TestWriter) Write(p []byte) (n int, err error) {
	t.Written = p
	return 0, nil
}

type TestFileList struct {
	testList []os.FileInfo
}

func (t *TestFileList) Readdir(n int) ([]os.FileInfo, error) {
	return t.testList, nil
}

type TestFileInfo struct {
	isDir bool
	name  string
}

func (t *TestFileInfo) IsDir() bool {
	return t.isDir
}

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

func (t *TestFileInfo) Size() int64 {
	return 0
}

func (t *TestFileInfo) Mode() os.FileMode {
	return 0
}

func (t *TestFileInfo) ModTime() time.Time {
	return time.Now()
}

func (t *TestFileInfo) Sys() interface{} {
	return nil
}

func TestFindDefaultDir(t *testing.T) {
	Convey("Test Finding Default Dirs", t, func() {
		Convey("With no files or directories", func() {
			t := TestFileList{}
			_, err := findDefaultDir(&t)
			So(err.Error(), ShouldEqual, "No saved credentials found; please run 'credulous save' first")
		})
		Convey("With one file and no directories", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: false})
			t := TestFileList{testList: i}
			_, err := findDefaultDir(&t)
			So(err, ShouldNotEqual, nil)
			So(err.Error(), ShouldEqual, "No saved credentials found; please run 'credulous save' first")
		})
		Convey("With one file and one directory", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: false})
			i = append(i, &TestFileInfo{isDir: true, name: "foo"})
			t := TestFileList{testList: i}
			name, err := findDefaultDir(&t)
			So(err, ShouldEqual, nil)
			So(name, ShouldEqual, "foo")
		})
		Convey("With no files and more than one directory", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: true, name: "foo"})
			i = append(i, &TestFileInfo{isDir: true, name: "bar"})
			i = append(i, &TestFileInfo{isDir: true, name: "baz"})
			t := TestFileList{testList: i}
			_, err := findDefaultDir(&t)
			So(err, ShouldNotEqual, nil)
			So(err.Error(), ShouldEqual, "More than one account found; please specify account and user")
		})
	})
}

func TestReadFile(t *testing.T) {
	Convey("Test Read File", t, func() {
		Convey("Valid Json returns Credential", func() {

			tmp, err := ioutil.ReadFile("testkey")
			panic_the_err(err)
			key, err := ssh.ParseRawPrivateKey(tmp)
			panic_the_err(err)
			privkey := key.(*rsa.PrivateKey)

			cred := readCredentialFile("credential.json", privkey)
			So(cred.LifeTime, ShouldEqual, 22)
		})
		Convey("Credentials display correctly", func() {
			cred := Credential{KeyId: "ABC", SecretKey: "SECRET"}
			testWriter := TestWriter{}
			cred.Display(&testWriter)
			So(string(testWriter.Written), ShouldEqual, "export AWS_ACCESS_KEY_ID=ABC\nexport AWS_SECRET_ACCESS_KEY=SECRET\n")
		})

		Convey("Saving credentials", func() {
			// temp_dir, err := ioutil.TempDir("", "SavingCredentialsTest")
			// panic_the_err(err)
			//
			// cred := Credential{KeyId: "ABC", SecretKey: "SECRET"}
			// new_filename := Save(cred, temp_dir)
			//
			// new_cred := readCredentialFile(new_filename)
			// fmt.Println(new_cred)

		})
	})
}
<commit_msg>Add tests for getDirs<commit_after>package main

import (
	"crypto/rsa"
	"io/ioutil"
	"os"
	"testing"
	"time"

	"code.google.com/p/go.crypto/ssh"
	. "github.com/smartystreets/goconvey/convey"
	// "io/ioutil"
	// "fmt"
)

type TestWriter struct {
	Written []byte
}

func (t *TestWriter) Write(p []byte) (n int, err error) {
	t.Written = p
	return 0, nil
}

type TestFileList struct {
	testList []os.FileInfo
}

func (t *TestFileList) Readdir(n int) ([]os.FileInfo, error) {
	return t.testList, nil
}

type TestFileInfo struct {
	isDir bool
	name  string
}

func (t *TestFileInfo) IsDir() bool {
	return t.isDir
}

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

func (t *TestFileInfo) Size() int64 {
	return 0
}

func (t *TestFileInfo) Mode() os.FileMode {
	return 0
}

func (t *TestFileInfo) ModTime() time.Time {
	return time.Now()
}

func (t *TestFileInfo) Sys() interface{} {
	return nil
}

func TestGetDirs(t *testing.T) {
	Convey("Test finding all dirs", t, func() {
		Convey("Test with nothing", func() {
			t := TestFileList{}
			ents, err := getDirs(&t)
			So(err, ShouldEqual, nil)
			So(len(ents), ShouldEqual, 0)
		})
		Convey("Test with files only", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: false})
			i = append(i, &TestFileInfo{isDir: false})
			i = append(i, &TestFileInfo{isDir: false})
			t := TestFileList{testList: i}
			ents, err := getDirs(&t)
			So(err, ShouldEqual, nil)
			So(len(ents), ShouldEqual, 0)
		})
		Convey("Test with one dir", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: true})
			t := TestFileList{testList: i}
			ents, err := getDirs(&t)
			So(err, ShouldEqual, nil)
			So(len(ents), ShouldEqual, 1)
		})
		Convey("Test with multiple dirs", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: true})
			i = append(i, &TestFileInfo{isDir: true})
			i = append(i, &TestFileInfo{isDir: true})
			i = append(i, &TestFileInfo{isDir: true})
			t := TestFileList{testList: i}
			ents, err := getDirs(&t)
			So(err, ShouldEqual, nil)
			So(len(ents), ShouldEqual, 4)
		})
	})
}

func TestFindDefaultDir(t *testing.T) {
	Convey("Test Finding Default Dirs", t, func() {
		Convey("With no files or directories", func() {
			t := TestFileList{}
			_, err := findDefaultDir(&t)
			So(err.Error(), ShouldEqual, "No saved credentials found; please run 'credulous save' first")
		})
		Convey("With one file and no directories", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: false})
			t := TestFileList{testList: i}
			_, err := findDefaultDir(&t)
			So(err, ShouldNotEqual, nil)
			So(err.Error(), ShouldEqual, "No saved credentials found; please run 'credulous save' first")
		})
		Convey("With one file and one directory", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: false})
			i = append(i, &TestFileInfo{isDir: true, name: "foo"})
			t := TestFileList{testList: i}
			name, err := findDefaultDir(&t)
			So(err, ShouldEqual, nil)
			So(name, ShouldEqual, "foo")
		})
		Convey("With no files and more than one directory", func() {
			i := []os.FileInfo{}
			i = append(i, &TestFileInfo{isDir: true, name: "foo"})
			i = append(i, &TestFileInfo{isDir: true, name: "bar"})
			i = append(i, &TestFileInfo{isDir: true, name: "baz"})
			t := TestFileList{testList: i}
			_, err := findDefaultDir(&t)
			So(err, ShouldNotEqual, nil)
			So(err.Error(), ShouldEqual, "More than one account found; please specify account and user")
		})
	})
}

func TestReadFile(t *testing.T) {
	Convey("Test Read File", t, func() {
		Convey("Valid Json returns Credential", func() {

			tmp, err := ioutil.ReadFile("testkey")
			panic_the_err(err)
			key, err := ssh.ParseRawPrivateKey(tmp)
			panic_the_err(err)
			privkey := key.(*rsa.PrivateKey)

			cred := readCredentialFile("credential.json", privkey)
			So(cred.LifeTime, ShouldEqual, 22)
		})
		Convey("Credentials display correctly", func() {
			cred := Credential{KeyId: "ABC", SecretKey: "SECRET"}
			testWriter := TestWriter{}
			cred.Display(&testWriter)
			So(string(testWriter.Written), ShouldEqual, "export AWS_ACCESS_KEY_ID=ABC\nexport AWS_SECRET_ACCESS_KEY=SECRET\n")
		})

		Convey("Saving credentials", func() {
			// temp_dir, err := ioutil.TempDir("", "SavingCredentialsTest")
			// panic_the_err(err)
			//
			// cred := Credential{KeyId: "ABC", SecretKey: "SECRET"}
			// new_filename := Save(cred, temp_dir)
			//
			// new_cred := readCredentialFile(new_filename)
			// fmt.Println(new_cred)

		})
	})
}
<|endoftext|>


<commit_before>// +build linux

package water

// PlatformSpecificParams defines parameters in Config that are specific to
// Linux. A zero-value of such type is valid, yielding an interface
// with OS defined name.
type PlatformSpecificParams struct {
	// Name is the name to be set for the interface to be created. This overrides
	// the default name assigned by OS such as tap0 or tun0. A zero-value of this
	// field, i.e. an empty string, indicates that the default name should be
	// used.
	Name string

	// Enable or disable persistence mode for the interface device.
	Persist bool

	// ID of the user which will be granted ownership of the device.
	// The default value of -1 specifies that any user may use the device.
	Owner int

	// ID of the group which will be granted access to the device.
	// The default value of -1 specifies that any group may use the device.
	Group int
}

func defaultPlatformSpecificParams() PlatformSpecificParams {
	return PlatformSpecificParams{
		Owner: -1,
		Group: -1,
	}
}
<commit_msg>linux: Rewords Documentation for Owner/Group Param<commit_after>// +build linux

package water

// PlatformSpecificParams defines parameters in Config that are specific to
// Linux. A zero-value of such type is valid, yielding an interface
// with OS defined name.
type PlatformSpecificParams struct {
	// Name is the name to be set for the interface to be created. This overrides
	// the default name assigned by OS such as tap0 or tun0. A zero-value of this
	// field, i.e. an empty string, indicates that the default name should be
	// used.
	Name string

	// Enable or disable persistence mode for the interface device.
	Persist bool

	// ID of the user which will be granted ownership of the device.
	// If set to a negative value, the owner value will not be changed.
	// By default, Linux sets the owner to -1, which allows any user.
	Owner int

	// ID of the group which will be granted access to the device.
	// If set to a negative value, the group value will not be changed.
	// By default, Linux sets the group to -1, which allows any group.
	Group int
}

func defaultPlatformSpecificParams() PlatformSpecificParams {
	return PlatformSpecificParams{
		Owner: -1,
		Group: -1,
	}
}
<|endoftext|>


<commit_before>package parser

import (
	"encoding/binary"
	"errors"
	"io"

	"github.com/jstesta/gomidi/cfg"
	"github.com/jstesta/gomidi/midi"
	"github.com/jstesta/gomidi/vlq"
)

func readTrackChunk(r io.Reader, cfg cfg.GomidiConfig) (c midi.Chunk, err error) {

	ctx := cfg.LogContext.With("reader", "track")

	var length uint32
	err = binary.Read(r, cfg.ByteOrder, &length)
	if err != nil {
		return
	}

	// track how many bytes have been read for exit condition
	var bytesRead int
	var events []midi.Event = make([]midi.Event, 0)
	var previousEvent midi.Event = nil

	for bytesRead < int(length) {

		deltaTime, br, err := vlq.ReadVLQ(r)
		if err != nil {
			return nil, err
		}
		bytesRead += br

		b := []byte{0}
		_, err = io.ReadFull(r, b)
		if err != nil {
			return nil, err
		}
		eventType := b[0]
		bytesRead++

		switch eventType {

		case 0xF0, 0xF7:
			event, br, err := readSysexEvent(r, deltaTime, cfg)
			if err != nil {
				return nil, err
			}
			bytesRead += br
			ctx.Log("event", event)
			events = append(events, event)
			previousEvent = event

		case 0xFF:
			event, br, err := readMetaEvent(r, deltaTime, cfg)
			if err != nil {
				return nil, err
			}
			bytesRead += br
			ctx.Log("event", event)
			events = append(events, event)
			previousEvent = event

		default:
			event, br, err := readMidiEvent(r, deltaTime, eventType, previousEvent, cfg)
			if err != nil {
				return nil, err
			}
			bytesRead += br
			ctx.Log("event", event)
			events = append(events, event)
			previousEvent = event

		}

		if bytesRead > int(length) {
			return nil, errors.New("parsed too many bytes")
		}
	}

	return &midi.Track{events}, nil
}

func readSysexEvent(r io.Reader, deltaTime int, cfg cfg.GomidiConfig) (e midi.Event, bytesRead int, err error) {

	ctx := cfg.LogContext.With("reader", "sysex")

	length, br, err := vlq.ReadVLQ(r)
	if err != nil {
		return
	}
	bytesRead += br

	ctx.Log("datalen", length)
	data := make([]byte, length)
	_, err = io.ReadFull(r, data)
	bytesRead += length

	e = midi.NewSysexEvent(deltaTime, data)

	return
}

func readMetaEvent(r io.Reader, deltaTime int, cfg cfg.GomidiConfig) (e midi.Event, bytesRead int, err error) {

	ctx := cfg.LogContext.With("reader", "meta")

	b := []byte{0}
	_, err = io.ReadFull(r, b)
	if err != nil {
		return
	}
	metaEventType := b[0]
	bytesRead++
	ctx.Log("type", metaEventType)

	length, br, err := vlq.ReadVLQ(r)
	if err != nil {
		return
	}
	bytesRead += br

	ctx.Log("datalen", length)
	data := make([]byte, length)
	_, err = io.ReadFull(r, data)
	bytesRead += length

	e = midi.NewMetaEvent(deltaTime, metaEventType, data)

	return
}

func readMidiEvent(r io.Reader, deltaTime int, status byte, prev midi.Event, cfg cfg.GomidiConfig) (e midi.Event, bytesRead int, err error) {

	ctx := cfg.LogContext.With("reader", "midi")

	switch status >> 4 {

	case 0x8, 0x9, 0xA, 0xB, 0xE:
		data := make([]byte, 2)
		_, err = io.ReadFull(r, data)
		if err != nil {
			return
		}
		bytesRead += 2
		e = midi.NewMidiEvent(deltaTime, status, data)

	case 0xC, 0xD:
		data := make([]byte, 1)
		_, err = io.ReadFull(r, data)
		if err != nil {
			return
		}
		bytesRead += 1
		e = midi.NewMidiEvent(deltaTime, status, data)

	default:
		if prev == nil {
			ctx.Log("skipped unknown MIDI event, type", status>>4)
		}

		if prevMidiEvent, ok := prev.(*midi.MidiEvent); ok {
			ctx.Log("no status MIDI event, using previous status of: %X", prevMidiEvent.Status())
			return readMidiEvent(r, deltaTime, prevMidiEvent.Status(), nil, cfg)
		}

		ctx.Log("this is bad! fixme")

	}

	return
}
<commit_msg>Prototype for new midi track reader - WIP<commit_after>package parser

import (
	"encoding/binary"
	"errors"
	"io"

	"github.com/jstesta/gomidi/cfg"
	"github.com/jstesta/gomidi/midi"
	"github.com/jstesta/gomidi/util"
	"github.com/jstesta/gomidi/vlq"
)

func readTrackChunk(r io.Reader, cfg cfg.GomidiConfig) (c midi.Chunk, err error) {

	ctx := cfg.LogContext.With("reader", "track")

	var length uint32
	err = binary.Read(r, cfg.ByteOrder, &length)
	if err != nil {
		return
	}

	// track how many bytes have been read for exit condition
	var bytesRead int
	var events []midi.Event = make([]midi.Event, 0)
	var previousEvent midi.Event = nil

	for bytesRead < int(length) {

		deltaTime, br, err := vlq.ReadVLQ(r)
		if err != nil {
			return nil, err
		}
		bytesRead += br

		b := []byte{0}
		_, err = io.ReadFull(r, b)
		if err != nil {
			return nil, err
		}
		eventType := b[0]
		bytesRead++

		switch eventType {

		case 0xF0, 0xF7:
			event, br, err := readSysexEvent(r, deltaTime, cfg)
			if err != nil {
				return nil, err
			}
			bytesRead += br
			ctx.Log("event", event)
			events = append(events, event)
			previousEvent = event

		case 0xFF:
			event, br, err := readMetaEvent(r, deltaTime, cfg)
			if err != nil {
				return nil, err
			}
			bytesRead += br
			ctx.Log("event", event)
			events = append(events, event)
			previousEvent = event

		default:
			event, br, err := readMidiEvent(r, deltaTime, eventType, previousEvent, nil, cfg)
			if err != nil {
				return nil, err
			}
			bytesRead += br
			ctx.Log("event", event)
			events = append(events, event)
			previousEvent = event

		}

		if bytesRead > int(length) {
			return nil, errors.New("parsed too many bytes")
		}
	}

	return &midi.Track{events}, nil
}

func readSysexEvent(r io.Reader, deltaTime int, cfg cfg.GomidiConfig) (e midi.Event, bytesRead int, err error) {

	length, br, err := vlq.ReadVLQ(r)
	if err != nil {
		return
	}
	bytesRead += br

	data := make([]byte, length)
	_, err = io.ReadFull(r, data)
	bytesRead += length

	e = midi.NewSysexEvent(deltaTime, data)

	return
}

func readMetaEvent(r io.Reader, deltaTime int, cfg cfg.GomidiConfig) (e midi.Event, bytesRead int, err error) {

	b := []byte{0}
	_, err = io.ReadFull(r, b)
	if err != nil {
		return
	}
	metaEventType := b[0]
	bytesRead++

	length, br, err := vlq.ReadVLQ(r)
	if err != nil {
		return
	}
	bytesRead += br

	data := make([]byte, length)
	_, err = io.ReadFull(r, data)
	bytesRead += length

	e = midi.NewMetaEvent(deltaTime, metaEventType, data)

	return
}

func readMidiEvent(r io.Reader, deltaTime int, status byte, prev midi.Event, prefix []byte, cfg cfg.GomidiConfig) (e midi.Event, bytesRead int, err error) {

	subStatus := status >> 4
	ctx := cfg.LogContext.With("reader", "midi", "status", toolbox.ToBitString(status), "subStatus", toolbox.ToBitString(subStatus))

	if subStatus < 0xF && subStatus >= 0x8 {

		switch subStatus {

		case 0x8, 0x9, 0xA, 0xB, 0xE:
			n, data, err := readSome(r, prefix, 2)
			if err != nil {
				return nil, bytesRead, err
			}
			bytesRead += n
			e = midi.NewMidiEvent(deltaTime, status, data)

		case 0xC, 0xD:
			n, data, err := readSome(r, prefix, 1)
			if err != nil {
				return nil, bytesRead, err
			}
			bytesRead += n
			e = midi.NewMidiEvent(deltaTime, status, data)

		default:
			ctx.Log("warning", "unexpected channel voice msg")
		}
	} else if subStatus == 0xF {
		switch status & 0xF {

		case 0x0:
			n, w, err := readSome(r, prefix, 1)
			if err != nil {
				return nil, bytesRead, err
			}
			bytesRead += n

			mada := w[0] != 0xF7
			for mada {
				n, w, err := readSome(r, prefix, 1)
				if err != nil {
					return nil, bytesRead, err
				}
				bytesRead += n
				mada = w[0] != 0xF7
			}

		case 0x2:
			n, data, err := readSome(r, prefix, 2)
			if err != nil {
				return nil, bytesRead, err
			}
			bytesRead += n
			e = midi.NewMidiEvent(deltaTime, status, data)

		case 0x3:
			n, data, err := readSome(r, prefix, 1)
			if err != nil {
				return nil, bytesRead, err
			}
			bytesRead += n
			e = midi.NewMidiEvent(deltaTime, status, data)

		default:
			ctx.Log("aaaa", "other")
		}
	} else {
		if prev == nil {
			return
		}

		if prevMidiEvent, ok := prev.(*midi.MidiEvent); ok {
			return readMidiEvent(r, deltaTime, prevMidiEvent.Status(), nil, []byte{status}, cfg)
		}

		ctx.Log("action", "this is bad! fixme")
	}

	return
}

func readSome(r io.Reader, prefix []byte, count int) (n int, data []byte, err error) {
	data = make([]byte, count)

	if prefix == nil {

		n, err = io.ReadFull(r, data)
		return
	} else {

		copy(data, prefix)
		if len(prefix) == count {
			return
		}

		remaining := count - len(prefix)

		tmp := make([]byte, remaining)
		n, err = io.ReadFull(r, tmp)
		if err != nil {
			return
		}
		copy(data[len(prefix):], tmp)
		return
	}
}
<|endoftext|>


<commit_before>package forecasting

import (
	"github.com/fxsjy/RF.go/RF"
	"io"
	"../data"
)

func buildData (data []Record) (inputs [][]interface{} , targets []float64){
	for i := 0; i<len(data); i++ {
		if data[i].null {
			break
		}
		row := make([]interface{},5)
		row[0]=data[i].Time
		row[1]=data[i].Radiation
		row[2]=data[i].Humidity
		row[3]=data[i].Temperature
		row[4]=data[i].Wind
		inputs = append(inputs,row)
		targets = append(targets,data[i].Power)
	}
	return
}

func learnCSV (file io.Reader, channel chan data.CSVRequest) *RF.Forest {
	ret := make(chan CVSData, 1)
	request := new(data.CSVRequest)
	request.Return = ret
	request.Request = file
	channel <- request
	var resp CSVData
	for {
		if resp <-ret {
			break
		}
	}
	inputs, targets := buildData(resp.Data)
	forest := RF.BuildForrest(inputs, targets, len(targets), len(inputs),1)
	return forest
}

<commit_msg>well, at least data compiles for now<commit_after>package forecasting

import (
	"github.com/fxsjy/RF.go/RF"
	"io"
	"../data"
)

func buildData (data []data.Record) (inputs [][]interface{} , targets []float64){
	for i := 0; i<len(data); i++ {
		if data[i].null {
			break
		}
		row := make([]interface{},5)
		row[0]=data[i].Time
		row[1]=data[i].Radiation
		row[2]=data[i].Humidity
		row[3]=data[i].Temperature
		row[4]=data[i].Wind
		inputs = append(inputs,row)
		targets = append(targets,data[i].Power)
	}
	return
}

func learnCSV (file io.Reader, channel chan *data.CSVRequest) *RF.Forest {
	ret := make(chan *CVSData, 1)
	request := new(data.CSVRequest)
	request.Return = ret
	request.Request = file
	channel <- request
	var resp CSVData
	for {
		if resp <-ret {
			break
		}
	}
	inputs, targets := buildData(resp.Data)
	forest := RF.BuildForrest(inputs, targets, len(targets), len(inputs),1)
	return forest
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"time"

	"github.com/gorilla/mux"

	"context"
)

// ThemeVersion is the version of the current CSS-JS theme.
// It is the name of the folder containing the theme files.
const ThemeVersion = "default"

// ThemeDate is the prefix used for "revving" the static files and enable long-term HTTP cache.
// It MUST end with underscore _ (see app.yaml)
const ThemeDate = "20211023_"

var r = mux.NewRouter()

func init() {
	initEnv()
	initToggles()
	initRoutes()

	// We want the random results to be different even if we reboot the server. Thus, we use
	// the clock to seed the default generator.
	// See https://programming-idioms.org/idiom/70/use-clock-as-random-generator-seed/346/go
	rand.Seed(time.Now().UnixNano())
}

func initRoutes() {
	if !toggles["online"] {
		handle("/", makeWall(" Under maintenance."))
		//r.HandleFunc("/", makeWall(" Coming soon."))
	} else {
		//handle("/", makeWall(" Coming soon."))
		handle("/", home)
		handle("/home", home)
		handle("/wall", makeWall(" Coming soon."))
		handle("/about", about)
		handle("/idiom/{idiomId}", idiomDetail)
		handle("/idiom/{idiomId}/impl/{implId}", idiomDetail)
		handle("/idiom/{idiomId}/{idiomTitle}", idiomDetail)
		handle("/idiom/{idiomId}/diff/{v1}/{v2}", versionDiff)
		handle("/idiom/{idiomId}/impl/{implId}/diff/{v1}/{v2}", versionDiff)
		handle("/idiom/{idiomId}/{idiomTitle}/{implId}/{implLang}", idiomDetail)
		handle("/history/{idiomId}", idiomHistory)
		handle("/history/{idiomId}/impl/{implId}", implHistory)
		handle("/revert", revertIdiomVersion)
		handle("/admin-history-restore", restoreIdiomVersion)
		handle("/all-idioms", allIdioms)
		handle("/random-idiom/having/{havingLang}", randomIdiomHaving)
		handle("/random-idiom/not-having/{notHavingLang}", randomIdiomNotHaving)
		handle("/random-idiom", randomIdiom)
		handle("/search", searchRedirect)
		handle("/search/{q}", search)
		handle("/list-by-language/{langs}", listByLanguage)
		handle("/missing-fields/{lang}", missingList)
		handle("/backlog/{lang}", backlogForLanguage)
		handle("/backlog/{lang}/block/curation", backlogBlockCuration)
		handle("/backlog/{lang}/block/docs-demos", backlogBlockDocsDemos)
		handle("/backlog/{lang}/block/missing-impl", backlogBlockMissingImpl)
		handle("/idiom-picture", idiomPicture)
		handle("/rss-recently-created", rssRecentlyCreated)
		handle("/rss-recently-updated", rssRecentlyUpdated)
		handle("/rss-recent-changes", rssRecentChanges)
		handle("/my/{nickname}/{langs}", bookmarkableUserURL)
		handle("/my/{langs}", bookmarkableUserURL)
		// handle("/cheatsheet/{lang}", cheatsheet)
		handle("/cheatsheet/{langs:.+}", cheatsheetMulti)
		r.PathPrefix("/cheatsheet").HandlerFunc(cheatsheetPageRedirect)
		handleAjax("/typeahead-languages", typeaheadLanguages)
		handleAjax("/supported-languages", supportedLanguages)
		handleAjax("/ajax-other-implementations", ajaxOtherImplementations)
		handleAjax("/ajax-impl-flag/{idiomId}/{implId}", ajaxImplFlag)
		if toggles["writable"] {
			// When not in "read-only" mode
			handle("/idiom-save", idiomSave)
			handle("/idiom-edit/{idiomId}", idiomEdit)
			handle("/idiom-add-picture/{idiomId}", idiomAddPicture)
			handle("/idiom-save-picture", idiomSavePicture)
			handle("/impl-edit/{idiomId}/{implId}", implEdit)
			//handle("/fake-idiom-save", fakeIdiomSave)
			handle("/idiom-create", idiomCreate)
			handle("/impl-create/{idiomId}", implCreate)
			handle("/impl-create/{idiomId}/{lang}", implCreate)
			handle("/impl-save", implSave)
			// Ajax
			handleAjax("/ajax-idiom-vote", ajaxIdiomVote)
			handleAjax("/ajax-impl-vote", ajaxImplVote)
			handleAjax("/ajax-demo-site-suggest", ajaxDemoSiteSuggest)
			handleAjax("/ajax-user-message-box", userMessageBoxAjax)
			handleAjax("/ajax-dismiss-user-message", dismissUserMessage)
			handle("/about-block-project", ajaxAboutProject)
			handle("/about-block-all-idioms", ajaxAboutAllIdioms)
			handle("/about-block-language-coverage", ajaxAboutLanguageCoverage)
			handle("/about-block-rss", ajaxAboutRss)
			handle("/about-block-cheatsheets", ajaxAboutCheatsheets)
			handle("/about-block-backlogs", ajaxAboutBacklogs)
			handle("/about-block-see-also", ajaxAboutSeeAlso)
			handle("/about-block-contact", ajaxAboutContact)
			// Admin
			handle("/admin", admin)
			handle("/admin-data-export", adminExport)
			handle("/admin-data-import", adminImport)
			handle("/admin-resave-entities", adminResaveEntities)
			handle("/admin-flagged", adminListFlaggedContent)
			handleAjax("/admin-repair-history-versions", adminRepairHistoryVersions)
			handleAjax("/admin-data-import-ajax", adminImportAjax)
			handleAjax("/admin-reindex-ajax", adminReindexAjax)
			handleAjax("/admin-refresh-toggles-ajax", ajaxRefreshToggles)
			handleAjax("/admin-set-toggle-ajax", ajaxSetToggle)
			handleAjax("/admin-create-relation-ajax", ajaxCreateRelation)
			handleAjax("/admin-idiom-delete", idiomDelete)
			handleAjax("/admin-impl-delete", implDelete)
			handleAjax("/admin-send-message-for-user", sendMessageForUserAjax)
			handleAjax("/admin-flag-resolve", ajaxAdminFlagResolve)
			handleAjax("/admin-memcache-flush", ajaxAdminMemcacheFlush)
		}
		handleAjax("/api/idiom/{idiomId}", jsonIdiom)
		handleAjax("/api/idioms/all", jsonAllIdioms)
		handleAjax("/api/search/{q}", jsonSearch)
		r.PathPrefix("/using/").HandlerFunc(using)

		handle("/auth", handleAuth)
		handle("/_ah/login_required", handleAuth)
	}
	http.Handle("/", r)
}

// Request will fail if path parameters are missing
var neededPathVariables = map[string][]string{
	"/idiom/{idiomId}":                                  {"idiomId"},
	"/idiom/{idiomId}/impl/{implId}":                    {"idiomId"},
	"/idiom/{idiomId}/{idiomTitle}":                     {"idiomId"},
	"/idiom/{idiomId}/{idiomTitle}/{implId}/{implLang}": {"idiomId"},
	"/search/{q}":                                       {"q"},
	"/my/{nickname}/{langs}":                            {"nickname", "langs"},
	"/idiom-edit/{idiomId}":                             {"idiomId"},
	"/idiom-add-picture/{idiomId}":                      {"idiomId"},
	"/impl-edit/{idiomId}/{implId}":                     {"idiomId", "implId"},
	"/impl-create/{idiomId}":                            {"idiomId"},
	"/impl-create/{idiomId}/{lang}":                     {"idiomId"},
	"/cheatsheet/{lang}":                                {"lang"},
}

// Request will fail if it doesn't provide the required GET or POST parameters
var neededParameters = map[string][]string{
	"/typeahead-languages":         { /*todo*/ },
	"/idiom-save":                  {"idiom_title"},
	"/idiom-save-picture":          { /*todo*/ },
	"/impl-save":                   {"idiom_id", "impl_code"},
	"/revert":                      {"idiomId", "version"},
	"/ajax-idiom-vote":             {"idiomId", "choice"},
	"/ajax-impl-vote":              {"implId", "choice"},
	"/ajax-demo-site-suggest":      { /*todo*/ },
	"/ajax-dismiss-user-message":   {"key"},
	"/admin-data-export":           { /*todo*/ },
	"/admin-data-import":           { /*todo*/ },
	"/admin-data-import-ajax":      { /*todo*/ },
	"/admin-set-toggle-ajax":       {"toggle", "value"},
	"/admin-create-relation-ajax":  {"idiomAId", "idiomBId"},
	"/admin-idiom-delete":          {"idiomId"},
	"/admin-impl-delete":           {"idiomId", "implId"},
	"/admin-send-message-for-user": {"username", "message"},
	"/admin-flag-resolve":          {"flagkey"},
	"/api/idiom":                   {"idiomId"},
}

// Request will fail if corresponding toggle is off
var neededToggles = map[string][]string{
	"/home":                         {"online"},
	"/search":                       {"searchable"},
	"/search/{q}":                   {"searchable"},
	"/idiom-save":                   {"writable"},
	"/idiom-edit/{idiomId}":         {"writable", "writable", "idiomEditing"},
	"/idiom-add-picture/{idiomId}":  {"writable", "idiomEditing"},
	"/idiom-save-picture":           {"writable", "idiomEditing"},
	"/impl-edit/{idiomId}/{implId}": {"writable", "implEditing"},
	"/idiom-create":                 {"writable"},
	"/impl-create/{idiomId}":        {"writable", "implAddition"},
	"/impl-create/{idiomId}/{lang}": {"writable", "implAddition"},
	"/impl-save":                    {"writable"},
	"/ajax-idiom-vote":              {"writable"},
	"/ajax-impl-vote":               {"writable"},
	"/admin":                        {"administrable"},
	"/admin-data-export":            {"administrable"},
	"/admin-data-import":            {"administrable"},
	"/admin-data-import-ajax":       {"administrable"},
	"/admin-set-toggle-ajax":        {"administrable"},
	"/admin-create-relation-ajax":   {"administrable"},
	"/admin-idiom-delete":           {"administrable"},
	"/admin-impl-delete":            {"administrable"},
}

type standardHandler func(w http.ResponseWriter, r *http.Request)
type betterHandler func(w http.ResponseWriter, r *http.Request) error

// Wrap HandleFunc with
// - error handling
// - mandatory path variables check
// - mandatory parameters check
// - toggles check
func handle(path string, h betterHandler) {
	r.HandleFunc(path,
		func(w http.ResponseWriter, r *http.Request) {
			if isSpam(w, r) {
				return
			}

			defer func() {
				if msg := recover(); msg != nil {
					errorPage(w, r, PiErrorf(http.StatusInternalServerError, "%v", msg))
					return
				}
			}()
			if configTime == "0" {
				ctx := r.Context()
				_ = refreshToggles(ctx)
				// If it fails... well, ignore for now and continue with non-fresh toggles.
			}

			if err := muxVarsMissing(w, r, neededPathVariables[path]...); err != nil {
				errorPage(w, r, err)
				return
			}
			if err := togglesMissing(w, r, neededToggles[path]...); err != nil {
				errorPage(w, r, err)
				return
			}
			if err := parametersMissing(w, r, neededParameters[path]...); err != nil {
				errorPage(w, r, err)
				return
			}
			err := h(w, r)
			if err != nil {
				errorPage(w, r, err)
			}
		})
}

func handleAjax(path string, h betterHandler) {
	r.HandleFunc(path,
		func(w http.ResponseWriter, r *http.Request) {
			if isSpam(w, r) {
				return
			}

			defer func() {
				if msg := recover(); msg != nil {
					errorJSON(w, r, PiErrorf(http.StatusInternalServerError, "%v", msg))
					return
				}
			}()
			if configTime == "0" {
				ctx := r.Context()
				_ = refreshToggles(ctx)
				// If it fails... well, ignore for now and continue with non-fresh toggles.
			}

			if err := muxVarsMissing(w, r, neededPathVariables[path]...); err != nil {
				errorJSON(w, r, err)
				return
			}
			if err := togglesMissing(w, r, neededToggles[path]...); err != nil {
				errorJSON(w, r, err)
				return
			}
			if err := parametersMissing(w, r, neededParameters[path]...); err != nil {
				errorJSON(w, r, err)
				return
			}

			// TODO: maybe we never want any other Content-Type so...?
			// w.Header().Set("Content-Type", "application/json")
			err := h(w, r)
			if err != nil {
				errorJSON(w, r, err)
			}
		})
}

var datastoreDao = GaeDatastoreAccessor{}
var memcachedDao = MemcacheDatastoreAccessor{datastoreDao}
var dao = memcachedDao

var daoVotes = GaeVotesAccessor{}

func parametersMissing(w http.ResponseWriter, r *http.Request, params ...string) error {
	missing := []string{}
	for _, param := range params {
		if r.FormValue(param) == "" {
			missing = append(missing, param)
		}
	}
	if len(missing) > 0 {
		return PiErrorf(http.StatusBadRequest, "Missing parameters : %s", missing)
	}
	return nil
}

// Looks in gorilla mux populated variables
func muxVarsMissing(w http.ResponseWriter, r *http.Request, params ...string) error {
	missing := []string{}
	muxvars := mux.Vars(r)
	for _, param := range params {
		if muxvars[param] == "" {
			missing = append(missing, param)
		}
	}
	if len(missing) > 0 {
		return PiErrorf(http.StatusBadRequest, "Missing parameters : %s", missing)
	}
	return nil
}

func validateURLFormat(urlStr string) error {
	u, err := url.Parse(urlStr)
	if err != nil {
		return err
	}
	if !u.IsAbs() {
		return fmt.Errorf("Requires an absolute URL")
	}
	return nil
}

func validateURLFormatOrEmpty(urlStr string) error {
	if urlStr == "" {
		return nil
	}
	return validateURLFormat(urlStr)
}

/*
func logIf(err error, logfunc func(format string, args ...interface{}), when string) {
	if err != nil {
		logfunc("Problem on %v: %v", when, err.Error())
	}
}
*/

func logIf(err error, logfunc func(ctx context.Context, format string, args ...interface{}), ctx context.Context, when string) {
	if err != nil {
		logfunc(ctx, "Problem on %v: %v", when, err.Error())
	}
}
<commit_msg>Changed assets, bumped ThemeDate.<commit_after>package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"time"

	"github.com/gorilla/mux"

	"context"
)

// ThemeVersion is the version of the current CSS-JS theme.
// It is the name of the folder containing the theme files.
const ThemeVersion = "default"

// ThemeDate is the prefix used for "revving" the static files and enable long-term HTTP cache.
// It MUST end with underscore _ (see app.yaml)
const ThemeDate = "20211026_"

var r = mux.NewRouter()

func init() {
	initEnv()
	initToggles()
	initRoutes()

	// We want the random results to be different even if we reboot the server. Thus, we use
	// the clock to seed the default generator.
	// See https://programming-idioms.org/idiom/70/use-clock-as-random-generator-seed/346/go
	rand.Seed(time.Now().UnixNano())
}

func initRoutes() {
	if !toggles["online"] {
		handle("/", makeWall(" Under maintenance."))
		//r.HandleFunc("/", makeWall(" Coming soon."))
	} else {
		//handle("/", makeWall(" Coming soon."))
		handle("/", home)
		handle("/home", home)
		handle("/wall", makeWall(" Coming soon."))
		handle("/about", about)
		handle("/idiom/{idiomId}", idiomDetail)
		handle("/idiom/{idiomId}/impl/{implId}", idiomDetail)
		handle("/idiom/{idiomId}/{idiomTitle}", idiomDetail)
		handle("/idiom/{idiomId}/diff/{v1}/{v2}", versionDiff)
		handle("/idiom/{idiomId}/impl/{implId}/diff/{v1}/{v2}", versionDiff)
		handle("/idiom/{idiomId}/{idiomTitle}/{implId}/{implLang}", idiomDetail)
		handle("/history/{idiomId}", idiomHistory)
		handle("/history/{idiomId}/impl/{implId}", implHistory)
		handle("/revert", revertIdiomVersion)
		handle("/admin-history-restore", restoreIdiomVersion)
		handle("/all-idioms", allIdioms)
		handle("/random-idiom/having/{havingLang}", randomIdiomHaving)
		handle("/random-idiom/not-having/{notHavingLang}", randomIdiomNotHaving)
		handle("/random-idiom", randomIdiom)
		handle("/search", searchRedirect)
		handle("/search/{q}", search)
		handle("/list-by-language/{langs}", listByLanguage)
		handle("/missing-fields/{lang}", missingList)
		handle("/backlog/{lang}", backlogForLanguage)
		handle("/backlog/{lang}/block/curation", backlogBlockCuration)
		handle("/backlog/{lang}/block/docs-demos", backlogBlockDocsDemos)
		handle("/backlog/{lang}/block/missing-impl", backlogBlockMissingImpl)
		handle("/idiom-picture", idiomPicture)
		handle("/rss-recently-created", rssRecentlyCreated)
		handle("/rss-recently-updated", rssRecentlyUpdated)
		handle("/rss-recent-changes", rssRecentChanges)
		handle("/my/{nickname}/{langs}", bookmarkableUserURL)
		handle("/my/{langs}", bookmarkableUserURL)
		// handle("/cheatsheet/{lang}", cheatsheet)
		handle("/cheatsheet/{langs:.+}", cheatsheetMulti)
		r.PathPrefix("/cheatsheet").HandlerFunc(cheatsheetPageRedirect)
		handleAjax("/typeahead-languages", typeaheadLanguages)
		handleAjax("/supported-languages", supportedLanguages)
		handleAjax("/ajax-other-implementations", ajaxOtherImplementations)
		handleAjax("/ajax-impl-flag/{idiomId}/{implId}", ajaxImplFlag)
		if toggles["writable"] {
			// When not in "read-only" mode
			handle("/idiom-save", idiomSave)
			handle("/idiom-edit/{idiomId}", idiomEdit)
			handle("/idiom-add-picture/{idiomId}", idiomAddPicture)
			handle("/idiom-save-picture", idiomSavePicture)
			handle("/impl-edit/{idiomId}/{implId}", implEdit)
			//handle("/fake-idiom-save", fakeIdiomSave)
			handle("/idiom-create", idiomCreate)
			handle("/impl-create/{idiomId}", implCreate)
			handle("/impl-create/{idiomId}/{lang}", implCreate)
			handle("/impl-save", implSave)
			// Ajax
			handleAjax("/ajax-idiom-vote", ajaxIdiomVote)
			handleAjax("/ajax-impl-vote", ajaxImplVote)
			handleAjax("/ajax-demo-site-suggest", ajaxDemoSiteSuggest)
			handleAjax("/ajax-user-message-box", userMessageBoxAjax)
			handleAjax("/ajax-dismiss-user-message", dismissUserMessage)
			handle("/about-block-project", ajaxAboutProject)
			handle("/about-block-all-idioms", ajaxAboutAllIdioms)
			handle("/about-block-language-coverage", ajaxAboutLanguageCoverage)
			handle("/about-block-rss", ajaxAboutRss)
			handle("/about-block-cheatsheets", ajaxAboutCheatsheets)
			handle("/about-block-backlogs", ajaxAboutBacklogs)
			handle("/about-block-see-also", ajaxAboutSeeAlso)
			handle("/about-block-contact", ajaxAboutContact)
			// Admin
			handle("/admin", admin)
			handle("/admin-data-export", adminExport)
			handle("/admin-data-import", adminImport)
			handle("/admin-resave-entities", adminResaveEntities)
			handle("/admin-flagged", adminListFlaggedContent)
			handleAjax("/admin-repair-history-versions", adminRepairHistoryVersions)
			handleAjax("/admin-data-import-ajax", adminImportAjax)
			handleAjax("/admin-reindex-ajax", adminReindexAjax)
			handleAjax("/admin-refresh-toggles-ajax", ajaxRefreshToggles)
			handleAjax("/admin-set-toggle-ajax", ajaxSetToggle)
			handleAjax("/admin-create-relation-ajax", ajaxCreateRelation)
			handleAjax("/admin-idiom-delete", idiomDelete)
			handleAjax("/admin-impl-delete", implDelete)
			handleAjax("/admin-send-message-for-user", sendMessageForUserAjax)
			handleAjax("/admin-flag-resolve", ajaxAdminFlagResolve)
			handleAjax("/admin-memcache-flush", ajaxAdminMemcacheFlush)
		}
		handleAjax("/api/idiom/{idiomId}", jsonIdiom)
		handleAjax("/api/idioms/all", jsonAllIdioms)
		handleAjax("/api/search/{q}", jsonSearch)
		r.PathPrefix("/using/").HandlerFunc(using)

		handle("/auth", handleAuth)
		handle("/_ah/login_required", handleAuth)
	}
	http.Handle("/", r)
}

// Request will fail if path parameters are missing
var neededPathVariables = map[string][]string{
	"/idiom/{idiomId}":                                  {"idiomId"},
	"/idiom/{idiomId}/impl/{implId}":                    {"idiomId"},
	"/idiom/{idiomId}/{idiomTitle}":                     {"idiomId"},
	"/idiom/{idiomId}/{idiomTitle}/{implId}/{implLang}": {"idiomId"},
	"/search/{q}":                                       {"q"},
	"/my/{nickname}/{langs}":                            {"nickname", "langs"},
	"/idiom-edit/{idiomId}":                             {"idiomId"},
	"/idiom-add-picture/{idiomId}":                      {"idiomId"},
	"/impl-edit/{idiomId}/{implId}":                     {"idiomId", "implId"},
	"/impl-create/{idiomId}":                            {"idiomId"},
	"/impl-create/{idiomId}/{lang}":                     {"idiomId"},
	"/cheatsheet/{lang}":                                {"lang"},
}

// Request will fail if it doesn't provide the required GET or POST parameters
var neededParameters = map[string][]string{
	"/typeahead-languages":         { /*todo*/ },
	"/idiom-save":                  {"idiom_title"},
	"/idiom-save-picture":          { /*todo*/ },
	"/impl-save":                   {"idiom_id", "impl_code"},
	"/revert":                      {"idiomId", "version"},
	"/ajax-idiom-vote":             {"idiomId", "choice"},
	"/ajax-impl-vote":              {"implId", "choice"},
	"/ajax-demo-site-suggest":      { /*todo*/ },
	"/ajax-dismiss-user-message":   {"key"},
	"/admin-data-export":           { /*todo*/ },
	"/admin-data-import":           { /*todo*/ },
	"/admin-data-import-ajax":      { /*todo*/ },
	"/admin-set-toggle-ajax":       {"toggle", "value"},
	"/admin-create-relation-ajax":  {"idiomAId", "idiomBId"},
	"/admin-idiom-delete":          {"idiomId"},
	"/admin-impl-delete":           {"idiomId", "implId"},
	"/admin-send-message-for-user": {"username", "message"},
	"/admin-flag-resolve":          {"flagkey"},
	"/api/idiom":                   {"idiomId"},
}

// Request will fail if corresponding toggle is off
var neededToggles = map[string][]string{
	"/home":                         {"online"},
	"/search":                       {"searchable"},
	"/search/{q}":                   {"searchable"},
	"/idiom-save":                   {"writable"},
	"/idiom-edit/{idiomId}":         {"writable", "writable", "idiomEditing"},
	"/idiom-add-picture/{idiomId}":  {"writable", "idiomEditing"},
	"/idiom-save-picture":           {"writable", "idiomEditing"},
	"/impl-edit/{idiomId}/{implId}": {"writable", "implEditing"},
	"/idiom-create":                 {"writable"},
	"/impl-create/{idiomId}":        {"writable", "implAddition"},
	"/impl-create/{idiomId}/{lang}": {"writable", "implAddition"},
	"/impl-save":                    {"writable"},
	"/ajax-idiom-vote":              {"writable"},
	"/ajax-impl-vote":               {"writable"},
	"/admin":                        {"administrable"},
	"/admin-data-export":            {"administrable"},
	"/admin-data-import":            {"administrable"},
	"/admin-data-import-ajax":       {"administrable"},
	"/admin-set-toggle-ajax":        {"administrable"},
	"/admin-create-relation-ajax":   {"administrable"},
	"/admin-idiom-delete":           {"administrable"},
	"/admin-impl-delete":            {"administrable"},
}

type standardHandler func(w http.ResponseWriter, r *http.Request)
type betterHandler func(w http.ResponseWriter, r *http.Request) error

// Wrap HandleFunc with
// - error handling
// - mandatory path variables check
// - mandatory parameters check
// - toggles check
func handle(path string, h betterHandler) {
	r.HandleFunc(path,
		func(w http.ResponseWriter, r *http.Request) {
			if isSpam(w, r) {
				return
			}

			defer func() {
				if msg := recover(); msg != nil {
					errorPage(w, r, PiErrorf(http.StatusInternalServerError, "%v", msg))
					return
				}
			}()
			if configTime == "0" {
				ctx := r.Context()
				_ = refreshToggles(ctx)
				// If it fails... well, ignore for now and continue with non-fresh toggles.
			}

			if err := muxVarsMissing(w, r, neededPathVariables[path]...); err != nil {
				errorPage(w, r, err)
				return
			}
			if err := togglesMissing(w, r, neededToggles[path]...); err != nil {
				errorPage(w, r, err)
				return
			}
			if err := parametersMissing(w, r, neededParameters[path]...); err != nil {
				errorPage(w, r, err)
				return
			}
			err := h(w, r)
			if err != nil {
				errorPage(w, r, err)
			}
		})
}

func handleAjax(path string, h betterHandler) {
	r.HandleFunc(path,
		func(w http.ResponseWriter, r *http.Request) {
			if isSpam(w, r) {
				return
			}

			defer func() {
				if msg := recover(); msg != nil {
					errorJSON(w, r, PiErrorf(http.StatusInternalServerError, "%v", msg))
					return
				}
			}()
			if configTime == "0" {
				ctx := r.Context()
				_ = refreshToggles(ctx)
				// If it fails... well, ignore for now and continue with non-fresh toggles.
			}

			if err := muxVarsMissing(w, r, neededPathVariables[path]...); err != nil {
				errorJSON(w, r, err)
				return
			}
			if err := togglesMissing(w, r, neededToggles[path]...); err != nil {
				errorJSON(w, r, err)
				return
			}
			if err := parametersMissing(w, r, neededParameters[path]...); err != nil {
				errorJSON(w, r, err)
				return
			}

			// TODO: maybe we never want any other Content-Type so...?
			// w.Header().Set("Content-Type", "application/json")
			err := h(w, r)
			if err != nil {
				errorJSON(w, r, err)
			}
		})
}

var datastoreDao = GaeDatastoreAccessor{}
var memcachedDao = MemcacheDatastoreAccessor{datastoreDao}
var dao = memcachedDao

var daoVotes = GaeVotesAccessor{}

func parametersMissing(w http.ResponseWriter, r *http.Request, params ...string) error {
	missing := []string{}
	for _, param := range params {
		if r.FormValue(param) == "" {
			missing = append(missing, param)
		}
	}
	if len(missing) > 0 {
		return PiErrorf(http.StatusBadRequest, "Missing parameters : %s", missing)
	}
	return nil
}

// Looks in gorilla mux populated variables
func muxVarsMissing(w http.ResponseWriter, r *http.Request, params ...string) error {
	missing := []string{}
	muxvars := mux.Vars(r)
	for _, param := range params {
		if muxvars[param] == "" {
			missing = append(missing, param)
		}
	}
	if len(missing) > 0 {
		return PiErrorf(http.StatusBadRequest, "Missing parameters : %s", missing)
	}
	return nil
}

func validateURLFormat(urlStr string) error {
	u, err := url.Parse(urlStr)
	if err != nil {
		return err
	}
	if !u.IsAbs() {
		return fmt.Errorf("Requires an absolute URL")
	}
	return nil
}

func validateURLFormatOrEmpty(urlStr string) error {
	if urlStr == "" {
		return nil
	}
	return validateURLFormat(urlStr)
}

/*
func logIf(err error, logfunc func(format string, args ...interface{}), when string) {
	if err != nil {
		logfunc("Problem on %v: %v", when, err.Error())
	}
}
*/

func logIf(err error, logfunc func(ctx context.Context, format string, args ...interface{}), ctx context.Context, when string) {
	if err != nil {
		logfunc(ctx, "Problem on %v: %v", when, err.Error())
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru 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"
	"github.com/tsuru/tsuru/cmd"
	"launchpad.net/gnuflag"
	"net/http"
	"strings"
)

type platformAdd struct {
	name       string
	dockerfile string
	fs         *gnuflag.FlagSet
}

func (p *platformAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-add",
		Usage:   "platform-add <platform name> [--dockerfile/-d Dockerfile]",
		Desc:    "Add new platform to tsuru.",
		MinArgs: 1,
	}
}

func (p *platformAdd) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
    body := fmt.Sprintf("name=%s&dockerfile=%s", name, p.dockerfile)
	url, err := cmd.GetURL("/platforms/add")
	request, err := http.NewRequest("PUT", url, strings.NewReader(body))
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	_, err = client.Do(request)
	if err != nil {
		return err
	}

	fmt.Fprintf(context.Stdout, "Platform successfully added!\n")
	return nil
}

func (p *platformAdd) Flags() *gnuflag.FlagSet {
	message := "The dockerfile url to create a platform"
	if p.fs == nil {
		p.fs = gnuflag.NewFlagSet("platform-add", gnuflag.ExitOnError)
		p.fs.StringVar(&p.dockerfile, "dockerfile", "", message)
		p.fs.StringVar(&p.dockerfile, "d", "", message)
	}

	return p.fs
}
<commit_msg>gofmt<commit_after>// Copyright 2014 tsuru 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"
	"github.com/tsuru/tsuru/cmd"
	"launchpad.net/gnuflag"
	"net/http"
	"strings"
)

type platformAdd struct {
	name       string
	dockerfile string
	fs         *gnuflag.FlagSet
}

func (p *platformAdd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "platform-add",
		Usage:   "platform-add <platform name> [--dockerfile/-d Dockerfile]",
		Desc:    "Add new platform to tsuru.",
		MinArgs: 1,
	}
}

func (p *platformAdd) Run(context *cmd.Context, client *cmd.Client) error {
	name := context.Args[0]
	body := fmt.Sprintf("name=%s&dockerfile=%s", name, p.dockerfile)
	url, err := cmd.GetURL("/platforms/add")
	request, err := http.NewRequest("PUT", url, strings.NewReader(body))
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	_, err = client.Do(request)
	if err != nil {
		return err
	}

	fmt.Fprintf(context.Stdout, "Platform successfully added!\n")
	return nil
}

func (p *platformAdd) Flags() *gnuflag.FlagSet {
	message := "The dockerfile url to create a platform"
	if p.fs == nil {
		p.fs = gnuflag.NewFlagSet("platform-add", gnuflag.ExitOnError)
		p.fs.StringVar(&p.dockerfile, "dockerfile", "", message)
		p.fs.StringVar(&p.dockerfile, "d", "", message)
	}

	return p.fs
}
<|endoftext|>


<commit_before>package plot

import (
	"bufio"
	"fmt"
	"github.com/smw1218/windygo/db"
	"io"
	"os"
	"os/exec"
	"time"
)

// mapping of direction to custom font
var cardinals map[int]string = map[int]string{
	0:   "", // N 		f100
	22:  "", // NNE	f101
	45:  "", // NE		f102
	67:  "", // ENE	f103
	90:  "", // E		f104
	112: "", // ESE	f105
	135: "", // SE		f106
	157: "", // SSE	f107
	180: "", // S		f108
	202: "", // SSW	f109
	225: "", // SW		f10a
	247: "", // WSW	f10b
	270: "", // W		f10c
	292: "", // WNW	f10d
	315: "", // NW		f10e
	337: "", // NNW	f10f
}

const summarySecondsForGraph = 300
const summarySecondsForGeneration = 60
const gpFormat = "%Y-%m-%d_%H:%M:%S"

type GnuPlot struct {
	// TODO mutex
	saved         []*db.Summary
	nextSave      int
	currentMinute *db.Summary
	summaryChan   chan *db.Summary
	ErrChan       chan error
}

func NewGnuPlot(mysql *db.Mysql) (*GnuPlot, error) {
	gp := &GnuPlot{}
	var err error
	gp.saved, err = mysql.GetSummaries(12*time.Hour, summarySecondsForGraph)
	if err != nil {
		return nil, err
	}
	gp.summaryChan = mysql.SavedChan
	gp.ErrChan = make(chan error, 1)
	go gp.generator()
	return gp, nil
}

func (gp *GnuPlot) generator() {
	for summary := range gp.summaryChan {
		if summary.SummarySeconds == summarySecondsForGraph {
			gp.saved[gp.nextSave%len(gp.saved)] = summary
			gp.nextSave++
		} else if summary.SummarySeconds == summarySecondsForGeneration {
			gp.currentMinute = summary
			gp.createPlot()
		}
	}
}

func (gp *GnuPlot) createPlot() {
	log.Printf("Creating plot")
	cmd := exec.Command("gnuplot")
	rd, wr := io.Pipe()
	cmd.Stdin = rd
	cmd.Stderr = os.Stderr
	go gp.writeData(bufio.NewWriter(wr), wr)
	err := cmd.Run()
	if err != nil {
		gp.sendError(fmt.Errorf("Error running gnuplot: %v", err))
	}
}

func (gp *GnuPlot) sendError(err error) {
	select {
	case gp.ErrChan <- err:
	default:
	}
}

func (gp *GnuPlot) writeData(w *bufio.Writer, closeme *io.PipeWriter) {
	defer closeme.Close()
	//write the script first
	_, err := w.WriteString(gnuPlotScript)
	if err != nil {
		gp.sendError(fmt.Errorf("Process write err: %v", err))
	}

	lensaved := len(gp.saved)
	// write the avg data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), summary.WindAvg))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")

	// write the lull data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), summary.WindLull))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")

	// write the gust data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), summary.WindGust))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")

	// write the direction data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil && summary.EndTime.Equal(summary.EndTime.Truncate(15*time.Minute)) {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), cardinals[summary.WindDirAvgCardinal()]))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")
}

const gnuPlotScript = `
set term png size 600, 400 truecolor enhanced
set output "windreport.png"
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set format x "%l%p\n%m/%d"
set autoscale xfixmin
set autoscale xfixmax
set autoscale x2fixmin
set autoscale x2fixmax
set xtics 3600 rangelimited
set mxtics 4
set grid xtics ytics mxtics
set style fill transparent solid 0.50 noborder
set arrow size 5, 45 front
plot [] [0:30<*] "-" using 1:2 title "Wind Avg (mph)" with filledcurves y1=0, \
 "-" using 1:2 title "Wind Lull" with lines \
 "-" using 1:2 title "Wind Gust" with lines \
 "-" using 1:(50):2 title "" with labels font "compass-arrows,24"
`
<commit_msg>forgot import<commit_after>package plot

import (
	"bufio"
	"fmt"
	"github.com/smw1218/windygo/db"
	"io"
	"log"
	"os"
	"os/exec"
	"time"
)

// mapping of direction to custom font
var cardinals map[int]string = map[int]string{
	0:   "", // N 		f100
	22:  "", // NNE	f101
	45:  "", // NE		f102
	67:  "", // ENE	f103
	90:  "", // E		f104
	112: "", // ESE	f105
	135: "", // SE		f106
	157: "", // SSE	f107
	180: "", // S		f108
	202: "", // SSW	f109
	225: "", // SW		f10a
	247: "", // WSW	f10b
	270: "", // W		f10c
	292: "", // WNW	f10d
	315: "", // NW		f10e
	337: "", // NNW	f10f
}

const summarySecondsForGraph = 300
const summarySecondsForGeneration = 60
const gpFormat = "%Y-%m-%d_%H:%M:%S"

type GnuPlot struct {
	// TODO mutex
	saved         []*db.Summary
	nextSave      int
	currentMinute *db.Summary
	summaryChan   chan *db.Summary
	ErrChan       chan error
}

func NewGnuPlot(mysql *db.Mysql) (*GnuPlot, error) {
	gp := &GnuPlot{}
	var err error
	gp.saved, err = mysql.GetSummaries(12*time.Hour, summarySecondsForGraph)
	if err != nil {
		return nil, err
	}
	gp.summaryChan = mysql.SavedChan
	gp.ErrChan = make(chan error, 1)
	go gp.generator()
	return gp, nil
}

func (gp *GnuPlot) generator() {
	for summary := range gp.summaryChan {
		if summary.SummarySeconds == summarySecondsForGraph {
			gp.saved[gp.nextSave%len(gp.saved)] = summary
			gp.nextSave++
		} else if summary.SummarySeconds == summarySecondsForGeneration {
			gp.currentMinute = summary
			gp.createPlot()
		}
	}
}

func (gp *GnuPlot) createPlot() {
	log.Printf("Creating plot")
	cmd := exec.Command("gnuplot")
	rd, wr := io.Pipe()
	cmd.Stdin = rd
	cmd.Stderr = os.Stderr
	go gp.writeData(bufio.NewWriter(wr), wr)
	err := cmd.Run()
	if err != nil {
		gp.sendError(fmt.Errorf("Error running gnuplot: %v", err))
	}
}

func (gp *GnuPlot) sendError(err error) {
	select {
	case gp.ErrChan <- err:
	default:
	}
}

func (gp *GnuPlot) writeData(w *bufio.Writer, closeme *io.PipeWriter) {
	defer closeme.Close()
	//write the script first
	_, err := w.WriteString(gnuPlotScript)
	if err != nil {
		gp.sendError(fmt.Errorf("Process write err: %v", err))
	}

	lensaved := len(gp.saved)
	// write the avg data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), summary.WindAvg))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")

	// write the lull data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), summary.WindLull))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")

	// write the gust data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), summary.WindGust))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")

	// write the direction data
	for i := 0; i < lensaved; i++ {
		summary := gp.saved[(i+gp.nextSave)%lensaved]
		if summary != nil && summary.EndTime.Equal(summary.EndTime.Truncate(15*time.Minute)) {
			_, err = w.WriteString(fmt.Sprintf("%s\t%v\n", summary.EndTime.Format(gpFormat), cardinals[summary.WindDirAvgCardinal()]))
			if err != nil {
				gp.sendError(fmt.Errorf("Process write err: %v", err))
			}
		}
	}
	w.WriteString("e\n")
}

const gnuPlotScript = `
set term png size 600, 400 truecolor enhanced
set output "windreport.png"
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set format x "%l%p\n%m/%d"
set autoscale xfixmin
set autoscale xfixmax
set autoscale x2fixmin
set autoscale x2fixmax
set xtics 3600 rangelimited
set mxtics 4
set grid xtics ytics mxtics
set style fill transparent solid 0.50 noborder
set arrow size 5, 45 front
plot [] [0:30<*] "-" using 1:2 title "Wind Avg (mph)" with filledcurves y1=0, \
 "-" using 1:2 title "Wind Lull" with lines \
 "-" using 1:2 title "Wind Gust" with lines \
 "-" using 1:(50):2 title "" with labels font "compass-arrows,24"
`
<|endoftext|>


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

package plugin

import (
	"github.com/henrylee2cn/goutil"
	tp "github.com/henrylee2cn/teleport"
	"github.com/henrylee2cn/teleport/socket"
)

// A proxy plugin for handling unknown pulling or pushing.

// Proxy creates a proxy plugin for handling unknown pulling and pushing.
func Proxy(caller Caller) tp.Plugin {
	return &proxy{
		pullFunc: caller.Pull,
		pushFunc: caller.Push,
	}
}

// Proxy creates a proxy plugin for handling unknown pulling.
func ProxyPull(fn PullFunc) tp.Plugin {
	return &proxy{pullFunc: fn}
}

// Proxy creates a proxy plugin for handling unknown pushing.
func ProxyPush(fn PushFunc) tp.Plugin {
	return &proxy{pushFunc: fn}
}

type (
	Caller interface {
		Pull(uri string, args interface{}, reply interface{}, setting ...socket.PacketSetting) tp.PullCmd
		Push(uri string, args interface{}, setting ...socket.PacketSetting) *tp.Rerror
	}
	PullFunc func(uri string, args interface{}, reply interface{}, setting ...socket.PacketSetting) tp.PullCmd
	PushFunc func(uri string, args interface{}, setting ...socket.PacketSetting) *tp.Rerror
	proxy    struct {
		peer     tp.Peer
		pullFunc PullFunc
		pushFunc PushFunc
	}
)

var (
	_ tp.PostNewPeerPlugin = new(proxy)
	_ tp.PostListenPlugin  = new(proxy)
)

func (p *proxy) Name() string {
	return "proxy"
}

func (p *proxy) PostNewPeer(peer tp.EarlyPeer) error {
	p.peer = peer.(tp.Peer)
	return nil
}

func (p *proxy) PostListen() error {
	if p.pullFunc != nil {
		p.peer.SetUnknownPull(p.pull)
	}
	if p.pushFunc != nil {
		p.peer.SetUnknownPush(p.push)
	}
	return nil
}

func (p *proxy) pull(ctx tp.UnknownPullCtx) (interface{}, *tp.Rerror) {
	var settings = make([]socket.PacketSetting, 0, 8)
	ctx.VisitMeta(func(key, value []byte) {
		settings = append(settings, socket.WithAddMeta(string(key), string(value)))
	})
	if len(ctx.PeekMeta(tp.MetaRealIp)) == 0 {
		settings = append(settings, socket.WithAddMeta(tp.MetaRealIp, ctx.Ip()))
	}
	var reply []byte
	pullcmd := p.pullFunc(ctx.Uri(), ctx.InputBodyBytes(), &reply, settings...)
	pullcmd.InputMeta().VisitAll(func(key, value []byte) {
		ctx.SetMeta(goutil.BytesToString(key), goutil.BytesToString(value))
	})
	return reply, pullcmd.Rerror()
}

func (p *proxy) push(ctx tp.UnknownPushCtx) *tp.Rerror {
	var settings = make([]socket.PacketSetting, 0, 8)
	ctx.VisitMeta(func(key, value []byte) {
		settings = append(settings, socket.WithAddMeta(string(key), string(value)))
	})
	if len(ctx.PeekMeta(tp.MetaRealIp)) == 0 {
		settings = append(settings, socket.WithAddMeta(tp.MetaRealIp, ctx.Ip()))
	}
	return p.pushFunc(ctx.Uri(), ctx.InputBodyBytes(), settings...)
}
<commit_msg>Add comments<commit_after>// Copyright 2017 HenryLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package plugin

import (
	"github.com/henrylee2cn/goutil"
	tp "github.com/henrylee2cn/teleport"
	"github.com/henrylee2cn/teleport/socket"
)

// A proxy plugin for handling unknown pulling or pushing.

// Proxy creates a proxy plugin for handling unknown pulling and pushing.
func Proxy(caller Caller) tp.Plugin {
	return &proxy{
		pullFunc: caller.Pull,
		pushFunc: caller.Push,
	}
}

// ProxyPull creates a proxy plugin for handling unknown pulling.
func ProxyPull(fn PullFunc) tp.Plugin {
	return &proxy{pullFunc: fn}
}

// ProxyPush creates a proxy plugin for handling unknown pushing.
func ProxyPush(fn PushFunc) tp.Plugin {
	return &proxy{pushFunc: fn}
}

type (
	// Caller the object used to pull and push
	Caller interface {
		Pull(uri string, args interface{}, reply interface{}, setting ...socket.PacketSetting) tp.PullCmd
		Push(uri string, args interface{}, setting ...socket.PacketSetting) *tp.Rerror
	}
	// PullFunc the function used to pull
	PullFunc func(uri string, args interface{}, reply interface{}, setting ...socket.PacketSetting) tp.PullCmd
	// PushFunc the function used to push
	PushFunc func(uri string, args interface{}, setting ...socket.PacketSetting) *tp.Rerror
	proxy    struct {
		peer     tp.Peer
		pullFunc PullFunc
		pushFunc PushFunc
	}
)

var (
	_ tp.PostNewPeerPlugin = new(proxy)
	_ tp.PostListenPlugin  = new(proxy)
)

func (p *proxy) Name() string {
	return "proxy"
}

func (p *proxy) PostNewPeer(peer tp.EarlyPeer) error {
	p.peer = peer.(tp.Peer)
	return nil
}

func (p *proxy) PostListen() error {
	if p.pullFunc != nil {
		p.peer.SetUnknownPull(p.pull)
	}
	if p.pushFunc != nil {
		p.peer.SetUnknownPush(p.push)
	}
	return nil
}

func (p *proxy) pull(ctx tp.UnknownPullCtx) (interface{}, *tp.Rerror) {
	var settings = make([]socket.PacketSetting, 0, 8)
	ctx.VisitMeta(func(key, value []byte) {
		settings = append(settings, socket.WithAddMeta(string(key), string(value)))
	})
	if len(ctx.PeekMeta(tp.MetaRealIp)) == 0 {
		settings = append(settings, socket.WithAddMeta(tp.MetaRealIp, ctx.Ip()))
	}
	var reply []byte
	pullcmd := p.pullFunc(ctx.Uri(), ctx.InputBodyBytes(), &reply, settings...)
	pullcmd.InputMeta().VisitAll(func(key, value []byte) {
		ctx.SetMeta(goutil.BytesToString(key), goutil.BytesToString(value))
	})
	return reply, pullcmd.Rerror()
}

func (p *proxy) push(ctx tp.UnknownPushCtx) *tp.Rerror {
	var settings = make([]socket.PacketSetting, 0, 8)
	ctx.VisitMeta(func(key, value []byte) {
		settings = append(settings, socket.WithAddMeta(string(key), string(value)))
	})
	if len(ctx.PeekMeta(tp.MetaRealIp)) == 0 {
		settings = append(settings, socket.WithAddMeta(tp.MetaRealIp, ctx.Ip()))
	}
	return p.pushFunc(ctx.Uri(), ctx.InputBodyBytes(), settings...)
}
<|endoftext|>


<commit_before>package plugin

import (
	"github.com/hashicorp/go-plugin"
	"github.com/hashicorp/terraform/terraform"
)

// The constants below are the names of the plugins that can be dispensed
// from the plugin server.
const (
	ProviderPluginName    = "provider"
	ProvisionerPluginName = "provisioner"
)

// Handshake is the HandshakeConfig used to configure clients and servers.
var Handshake = plugin.HandshakeConfig{
	// The ProtocolVersion is the version that must match between TF core
	// and TF plugins. This should be bumped whenever a change happens in
	// one or the other that makes it so that they can't safely communicate.
	// This could be adding a new interface value, it could be how
	// helper/schema computes diffs, etc.
	ProtocolVersion: 2,

	// The magic cookie values should NEVER be changed.
	MagicCookieKey:   "TF_PLUGIN_MAGIC_COOKIE",
	MagicCookieValue: "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2",
}

type ProviderFunc func() terraform.ResourceProvider
type ProvisionerFunc func() terraform.ResourceProvisioner

// ServeOpts are the configurations to serve a plugin.
type ServeOpts struct {
	ProviderFunc    ProviderFunc
	ProvisionerFunc ProvisionerFunc
}

// Serve serves a plugin. This function never returns and should be the final
// function called in the main function of the plugin.
func Serve(opts *ServeOpts) {
	plugin.Serve(&plugin.ServeConfig{
		HandshakeConfig: Handshake,
		Plugins:         pluginMap(opts),
	})
}

// pluginMap returns the map[string]plugin.Plugin to use for configuring a plugin
// server or client.
func pluginMap(opts *ServeOpts) map[string]plugin.Plugin {
	return map[string]plugin.Plugin{
		"provider":    &ResourceProviderPlugin{F: opts.ProviderFunc},
		"provisioner": &ResourceProvisionerPlugin{F: opts.ProvisionerFunc},
	}
}
<commit_msg>plugin: bump the protocol version due to Provisioner change<commit_after>package plugin

import (
	"github.com/hashicorp/go-plugin"
	"github.com/hashicorp/terraform/terraform"
)

// The constants below are the names of the plugins that can be dispensed
// from the plugin server.
const (
	ProviderPluginName    = "provider"
	ProvisionerPluginName = "provisioner"
)

// Handshake is the HandshakeConfig used to configure clients and servers.
var Handshake = plugin.HandshakeConfig{
	// The ProtocolVersion is the version that must match between TF core
	// and TF plugins. This should be bumped whenever a change happens in
	// one or the other that makes it so that they can't safely communicate.
	// This could be adding a new interface value, it could be how
	// helper/schema computes diffs, etc.
	ProtocolVersion: 3,

	// The magic cookie values should NEVER be changed.
	MagicCookieKey:   "TF_PLUGIN_MAGIC_COOKIE",
	MagicCookieValue: "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2",
}

type ProviderFunc func() terraform.ResourceProvider
type ProvisionerFunc func() terraform.ResourceProvisioner

// ServeOpts are the configurations to serve a plugin.
type ServeOpts struct {
	ProviderFunc    ProviderFunc
	ProvisionerFunc ProvisionerFunc
}

// Serve serves a plugin. This function never returns and should be the final
// function called in the main function of the plugin.
func Serve(opts *ServeOpts) {
	plugin.Serve(&plugin.ServeConfig{
		HandshakeConfig: Handshake,
		Plugins:         pluginMap(opts),
	})
}

// pluginMap returns the map[string]plugin.Plugin to use for configuring a plugin
// server or client.
func pluginMap(opts *ServeOpts) map[string]plugin.Plugin {
	return map[string]plugin.Plugin{
		"provider":    &ResourceProviderPlugin{F: opts.ProviderFunc},
		"provisioner": &ResourceProvisionerPlugin{F: opts.ProvisionerFunc},
	}
}
<|endoftext|>


<commit_before>package core

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

var impureFunction = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value {
		return NewOutput(ts[0])
	})

func TestThunkEvalWithNotCallable(t *testing.T) {
	assert.Equal(t, "TypeError", PApp(Nil).Eval().(ErrorType).name)
}

func TestThunkEvalWithImpureFunctionCall(t *testing.T) {
	assert.Equal(t, "TypeError", PApp(impureFunction, Nil).Eval().(ErrorType).name)
}

func TestThunkEvalByCallingError(t *testing.T) {
	e := PApp(NewError("Apple", "pen.")).Eval().(ErrorType)
	t.Log(e)
	assert.Equal(t, 1, len(e.callTrace))
}

func TestThunkEvalByCallingErrorTwice(t *testing.T) {
	e := PApp(PApp(NewError("Apple", "pen."))).Eval().(ErrorType)
	t.Log(e)
	assert.Equal(t, 2, len(e.callTrace))
}

func TestThunkEvalOutput(t *testing.T) {
	s := NewString("foo")
	assert.Equal(t, s.Eval(), PApp(impureFunction, s).EvalOutput())
}

func TestThunkEvalOutputWithNonOutput(t *testing.T) {
	for _, th := range []*Thunk{Nil, PApp(identity, Nil)} {
		v := th.EvalOutput()
		err, ok := v.(ErrorType)
		t.Logf("%#v\n", v)
		assert.True(t, ok)
		assert.Equal(t, err.name, "TypeError")
	}
}

func TestThunkEvalOutputWithError(t *testing.T) {
	v := OutOfRangeError().EvalOutput()
	err, ok := v.(ErrorType)
	t.Logf("%#v\n", v)
	assert.True(t, ok)
	assert.Equal(t, err.name, "OutOfRangeError")
}

func TestAssertValueIsNormal(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Fail()
		}
	}()

	assertValueIsNormal("This", Nil)
}
<commit_msg>Fix order of expected and actual values<commit_after>package core

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

var impureFunction = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value {
		return NewOutput(ts[0])
	})

func TestThunkEvalWithNotCallable(t *testing.T) {
	assert.Equal(t, "TypeError", PApp(Nil).Eval().(ErrorType).name)
}

func TestThunkEvalWithImpureFunctionCall(t *testing.T) {
	assert.Equal(t, "TypeError", PApp(impureFunction, Nil).Eval().(ErrorType).name)
}

func TestThunkEvalByCallingError(t *testing.T) {
	e := PApp(NewError("Apple", "pen.")).Eval().(ErrorType)
	t.Log(e)
	assert.Equal(t, 1, len(e.callTrace))
}

func TestThunkEvalByCallingErrorTwice(t *testing.T) {
	e := PApp(PApp(NewError("Apple", "pen."))).Eval().(ErrorType)
	t.Log(e)
	assert.Equal(t, 2, len(e.callTrace))
}

func TestThunkEvalOutput(t *testing.T) {
	s := NewString("foo")
	assert.Equal(t, s.Eval(), PApp(impureFunction, s).EvalOutput())
}

func TestThunkEvalOutputWithNonOutput(t *testing.T) {
	for _, th := range []*Thunk{Nil, PApp(identity, Nil)} {
		v := th.EvalOutput()
		err, ok := v.(ErrorType)
		t.Logf("%#v\n", v)
		assert.True(t, ok)
		assert.Equal(t, "TypeError", err.name)
	}
}

func TestThunkEvalOutputWithError(t *testing.T) {
	v := OutOfRangeError().EvalOutput()
	err, ok := v.(ErrorType)
	t.Logf("%#v\n", v)
	assert.True(t, ok)
	assert.Equal(t, "OutOfRangeError", err.name)
}

func TestAssertValueIsNormal(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Fail()
		}
	}()

	assertValueIsNormal("This", Nil)
}
<|endoftext|>


<commit_before>package desugar

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/gensym"
)

func flattenStatement(s interface{}) []interface{} {
	switch s := s.(type) {
	case ast.LetFunction:
		return flattenLetFunction(s)
	default:
		return []interface{}{s}
	}
}

func flattenStatements(old []interface{}) []interface{} {
	new := make([]interface{}, 0)

	for _, s := range old {
		new = append(new, flattenStatement(s)...)
	}

	return new
}

func flattenLetFunction(f ast.LetFunction) []interface{} {
	f = flattenInnerStatements(f)

	ss := make([]interface{}, 0)
	ls := make([]interface{}, 0)
	names := signatureToNames(f.Signature())

	for _, l := range f.Lets() {
		switch l := l.(type) {
		case ast.LetVar:
			ls = append(ls, l)
			names.add(l.Name())
		case ast.LetFunction:
			usedNames := names.find(l.Body())
			for _, l := range l.Lets() {
				usedNames.merge(names.find(l.(ast.LetVar)))
			}
			usedNames.subtract(signatureToNames(l.Signature()))
			usedNamesSlice := usedNames.slice()

			flattened := gensym.GenSym(f.Name(), l.Name())

			ss = append(ss, ast.NewLetFunction(
				flattened,
				prependPosReqsToSig(l.Signature(), usedNamesSlice),
				l.Lets(),
				l.Body(),
				l.DebugInfo()))

			ls = append(ls, ast.NewLetVar(
				l.Name(),
				ast.NewApp(
					"partial",
					ast.NewArguments(
						append(
							[]ast.PositionalArgument{ast.NewPositionalArgument(flattened, false)},
							namesToPosArgs(usedNamesSlice)...,
						), nil, nil),
					f.DebugInfo())))

			names.add(l.Name())
		default:
			panic(fmt.Errorf("Invalid value: %#v", l))
		}
	}

	return append(ss, ast.NewLetFunction(f.Name(), f.Signature(), ls, f.Body(), f.DebugInfo()))
}

func flattenInnerStatements(f ast.LetFunction) ast.LetFunction {
	return ast.NewLetFunction(
		f.Name(),
		f.Signature(),
		flattenStatements(f.Lets()),
		f.Body(),
		f.DebugInfo())
}
<commit_msg>Refactor flatten.go<commit_after>package desugar

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/gensym"
)

func flattenStatement(s interface{}) []interface{} {
	switch s := s.(type) {
	case ast.LetFunction:
		return flattenLetFunction(s)
	default:
		return []interface{}{s}
	}
}

func flattenStatements(old []interface{}) []interface{} {
	new := make([]interface{}, 0)

	for _, s := range old {
		new = append(new, flattenStatement(s)...)
	}

	return new
}

func flattenLetFunction(f ast.LetFunction) []interface{} {
	f = flattenInnerStatements(f)

	ss := make([]interface{}, 0)
	ls := make([]interface{}, 0)
	names := signatureToNames(f.Signature())

	for _, l := range f.Lets() {
		switch l := l.(type) {
		case ast.LetVar:
			ls = append(ls, l)
			names.add(l.Name())
		case ast.LetFunction:
			usedNames := names.find(l.Body())
			for _, l := range l.Lets() {
				usedNames.merge(names.find(l.(ast.LetVar)))
			}
			usedNames.subtract(signatureToNames(l.Signature()))
			usedNamesSlice := usedNames.slice()

			flattened := gensym.GenSym(f.Name(), l.Name())

			ss = append(ss, letFlattenedFunction(l, flattened, usedNamesSlice))

			ls = append(ls, ast.NewLetVar(
				l.Name(),
				ast.NewApp(
					"partial",
					ast.NewArguments(
						append(
							[]ast.PositionalArgument{ast.NewPositionalArgument(flattened, false)},
							namesToPosArgs(usedNamesSlice)...,
						), nil, nil),
					f.DebugInfo())))

			names.add(l.Name())
		default:
			panic(fmt.Errorf("Invalid value: %#v", l))
		}
	}

	return append(ss, ast.NewLetFunction(f.Name(), f.Signature(), ls, f.Body(), f.DebugInfo()))
}

func letFlattenedFunction(f ast.LetFunction, n string, args []string) ast.LetFunction {
	return ast.NewLetFunction(
		n,
		prependPosReqsToSig(f.Signature(), args),
		f.Lets(),
		f.Body(),
		f.DebugInfo())
}

func flattenInnerStatements(f ast.LetFunction) ast.LetFunction {
	return ast.NewLetFunction(
		f.Name(),
		f.Signature(),
		flattenStatements(f.Lets()),
		f.Body(),
		f.DebugInfo())
}
<|endoftext|>


<commit_before>// Copyright 2016 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 net

import (
	"errors"
	"os"
)

// If the ifindex is zero, interfaceTable returns mappings of all
// network interfaces. Otherwise it returns a mapping of a specific
// interface.
func interfaceTable(ifindex int) ([]Interface, error) {
	if ifindex == 0 {
		n, err := interfaceCount()
		if err != nil {
			return nil, err
		}
		ifcs := make([]Interface, n)
		for i := range ifcs {
			ifc, err := readInterface(i)
			if err != nil {
				return nil, err
			}
			ifcs[i] = *ifc
		}
		return ifcs, nil
	}

	ifc, err := readInterface(ifindex - 1)
	if err != nil {
		return nil, err
	}
	return []Interface{*ifc}, nil
}

func readInterface(i int) (*Interface, error) {
	ifc := &Interface{
		Index: i + 1,                        // Offset the index by one to suit the contract
		Name:  netdir + "/ipifc/" + itoa(i), // Name is the full path to the interface path in plan9
	}

	ifcstat := ifc.Name + "/status"
	ifcstatf, err := open(ifcstat)
	if err != nil {
		return nil, err
	}
	defer ifcstatf.close()

	line, ok := ifcstatf.readLine()
	if !ok {
		return nil, errors.New("invalid interface status file: " + ifcstat)
	}

	fields := getFields(line)
	if len(fields) < 4 {
		return nil, errors.New("invalid interface status file: " + ifcstat)
	}

	device := fields[1]
	mtustr := fields[3]

	mtu, _, ok := dtoi(mtustr)
	if !ok {
		return nil, errors.New("invalid status file of interface: " + ifcstat)
	}
	ifc.MTU = mtu

	// Not a loopback device
	if device != "/dev/null" {
		deviceaddrf, err := open(device + "/addr")
		if err != nil {
			return nil, err
		}
		defer deviceaddrf.close()

		line, ok = deviceaddrf.readLine()
		if !ok {
			return nil, errors.New("invalid address file for interface: " + device + "/addr")
		}

		if len(line) > 0 && len(line)%2 == 0 {
			ifc.HardwareAddr = make([]byte, len(line)/2)
			var ok bool
			for i := range ifc.HardwareAddr {
				j := (i + 1) * 2
				ifc.HardwareAddr[i], ok = xtoi2(line[i*2:j], 0)
				if !ok {
					ifc.HardwareAddr = ifc.HardwareAddr[:i]
					break
				}
			}
		}

		ifc.Flags = FlagUp | FlagBroadcast | FlagMulticast
	} else {
		ifc.Flags = FlagUp | FlagMulticast | FlagLoopback
	}

	return ifc, nil
}

func interfaceCount() (int, error) {
	d, err := os.Open(netdir + "/ipifc")
	if err != nil {
		return -1, err
	}
	defer d.Close()

	names, err := d.Readdirnames(0)
	if err != nil {
		return -1, err
	}

	// Assumes that numbered files in ipifc are strictly
	// the incrementing numbered directories for the
	// interfaces
	c := 0
	for _, name := range names {
		if _, _, ok := dtoi(name); !ok {
			continue
		}
		c++
	}

	return c, nil
}

// If the ifi is nil, interfaceAddrTable returns addresses for all
// network interfaces. Otherwise it returns addresses for a specific
// interface.
func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
	var ifcs []Interface
	if ifi == nil {
		var err error
		ifcs, err = interfaceTable(0)
		if err != nil {
			return nil, err
		}
	} else {
		ifcs = []Interface{*ifi}
	}

	var addrs []Addr
	for _, ifc := range ifcs {
		status := ifc.Name + "/status"
		statusf, err := open(status)
		if err != nil {
			return nil, err
		}
		defer statusf.close()

		// Read but ignore first line as it only contains the table header.
		// See https://9p.io/magic/man2html/3/ip
		if _, ok := statusf.readLine(); !ok {
			return nil, errors.New("cannot read header line for interface: " + status)
		}

		for line, ok := statusf.readLine(); ok; line, ok = statusf.readLine() {
			fields := getFields(line)
			if len(fields) < 1 {
				return nil, errors.New("cannot parse IP address for interface: " + status)
			}
			addr := fields[0]
			ip := ParseIP(addr)
			if ip == nil {
				return nil, errors.New("cannot parse IP address for interface: " + status)
			}

			// The mask is represented as CIDR relative to the IPv6 address.
			// Plan 9 internal representation is always IPv6.
			maskfld := fields[1]
			maskfld = maskfld[1:]
			pfxlen, _, ok := dtoi(maskfld)
			if !ok {
				return nil, errors.New("cannot parse network mask for interface: " + status)
			}
			var mask IPMask
			if ip.To4() != nil { // IPv4 or IPv6 IPv4-mapped address
				mask = CIDRMask(pfxlen-8*len(v4InV6Prefix), 8*IPv4len)
			}
			if ip.To16() != nil && ip.To4() == nil { // IPv6 address
				mask = CIDRMask(pfxlen, 8*IPv6len)
			}

			addrs = append(addrs, &IPNet{IP: ip, Mask: mask})
		}
	}

	return addrs, nil
}

// interfaceMulticastAddrTable returns addresses for a specific
// interface.
func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
	return nil, nil
}
<commit_msg>net: handle more interface types without a hardware address on Plan 9<commit_after>// Copyright 2016 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 net

import (
	"errors"
	"os"
)

// If the ifindex is zero, interfaceTable returns mappings of all
// network interfaces. Otherwise it returns a mapping of a specific
// interface.
func interfaceTable(ifindex int) ([]Interface, error) {
	if ifindex == 0 {
		n, err := interfaceCount()
		if err != nil {
			return nil, err
		}
		ifcs := make([]Interface, n)
		for i := range ifcs {
			ifc, err := readInterface(i)
			if err != nil {
				return nil, err
			}
			ifcs[i] = *ifc
		}
		return ifcs, nil
	}

	ifc, err := readInterface(ifindex - 1)
	if err != nil {
		return nil, err
	}
	return []Interface{*ifc}, nil
}

func readInterface(i int) (*Interface, error) {
	ifc := &Interface{
		Index: i + 1,                        // Offset the index by one to suit the contract
		Name:  netdir + "/ipifc/" + itoa(i), // Name is the full path to the interface path in plan9
	}

	ifcstat := ifc.Name + "/status"
	ifcstatf, err := open(ifcstat)
	if err != nil {
		return nil, err
	}
	defer ifcstatf.close()

	line, ok := ifcstatf.readLine()
	if !ok {
		return nil, errors.New("invalid interface status file: " + ifcstat)
	}

	fields := getFields(line)
	if len(fields) < 4 {
		return nil, errors.New("invalid interface status file: " + ifcstat)
	}

	device := fields[1]
	mtustr := fields[3]

	mtu, _, ok := dtoi(mtustr)
	if !ok {
		return nil, errors.New("invalid status file of interface: " + ifcstat)
	}
	ifc.MTU = mtu

	// Not a loopback device ("/dev/null") or packet interface (e.g. "pkt2")
	if stringsHasPrefix(device, netdir+"/") {
		deviceaddrf, err := open(device + "/addr")
		if err != nil {
			return nil, err
		}
		defer deviceaddrf.close()

		line, ok = deviceaddrf.readLine()
		if !ok {
			return nil, errors.New("invalid address file for interface: " + device + "/addr")
		}

		if len(line) > 0 && len(line)%2 == 0 {
			ifc.HardwareAddr = make([]byte, len(line)/2)
			var ok bool
			for i := range ifc.HardwareAddr {
				j := (i + 1) * 2
				ifc.HardwareAddr[i], ok = xtoi2(line[i*2:j], 0)
				if !ok {
					ifc.HardwareAddr = ifc.HardwareAddr[:i]
					break
				}
			}
		}

		ifc.Flags = FlagUp | FlagBroadcast | FlagMulticast
	} else {
		ifc.Flags = FlagUp | FlagMulticast | FlagLoopback
	}

	return ifc, nil
}

func interfaceCount() (int, error) {
	d, err := os.Open(netdir + "/ipifc")
	if err != nil {
		return -1, err
	}
	defer d.Close()

	names, err := d.Readdirnames(0)
	if err != nil {
		return -1, err
	}

	// Assumes that numbered files in ipifc are strictly
	// the incrementing numbered directories for the
	// interfaces
	c := 0
	for _, name := range names {
		if _, _, ok := dtoi(name); !ok {
			continue
		}
		c++
	}

	return c, nil
}

// If the ifi is nil, interfaceAddrTable returns addresses for all
// network interfaces. Otherwise it returns addresses for a specific
// interface.
func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
	var ifcs []Interface
	if ifi == nil {
		var err error
		ifcs, err = interfaceTable(0)
		if err != nil {
			return nil, err
		}
	} else {
		ifcs = []Interface{*ifi}
	}

	var addrs []Addr
	for _, ifc := range ifcs {
		status := ifc.Name + "/status"
		statusf, err := open(status)
		if err != nil {
			return nil, err
		}
		defer statusf.close()

		// Read but ignore first line as it only contains the table header.
		// See https://9p.io/magic/man2html/3/ip
		if _, ok := statusf.readLine(); !ok {
			return nil, errors.New("cannot read header line for interface: " + status)
		}

		for line, ok := statusf.readLine(); ok; line, ok = statusf.readLine() {
			fields := getFields(line)
			if len(fields) < 1 {
				return nil, errors.New("cannot parse IP address for interface: " + status)
			}
			addr := fields[0]
			ip := ParseIP(addr)
			if ip == nil {
				return nil, errors.New("cannot parse IP address for interface: " + status)
			}

			// The mask is represented as CIDR relative to the IPv6 address.
			// Plan 9 internal representation is always IPv6.
			maskfld := fields[1]
			maskfld = maskfld[1:]
			pfxlen, _, ok := dtoi(maskfld)
			if !ok {
				return nil, errors.New("cannot parse network mask for interface: " + status)
			}
			var mask IPMask
			if ip.To4() != nil { // IPv4 or IPv6 IPv4-mapped address
				mask = CIDRMask(pfxlen-8*len(v4InV6Prefix), 8*IPv4len)
			}
			if ip.To16() != nil && ip.To4() == nil { // IPv6 address
				mask = CIDRMask(pfxlen, 8*IPv6len)
			}

			addrs = append(addrs, &IPNet{IP: ip, Mask: mask})
		}
	}

	return addrs, nil
}

// interfaceMulticastAddrTable returns addresses for a specific
// interface.
func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
	return nil, nil
}
<|endoftext|>


<commit_before><commit_msg>Improve the assert docs<commit_after><|endoftext|>


<commit_before>package junos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"io/ioutil"
	"log"
	"strings"
)

// Junos holds the connection information to our Junos device.
type Junos struct {
	*netconf.Session
}

// rollbackXML parses our rollback diff configuration.
type rollbackXML struct {
	XMLName xml.Name `xml:"rollback-information"`
	Config  string   `xml:"configuration-information>configuration-output"`
}

// CommandXML parses our operational command responses.
type commandXML struct {
	Config string `xml:",innerxml"`
}

type software struct {
	RES []routingEngine `xml:"multi-routing-engine-item"`
}

type routingEngine struct {
	Name    string `xml:"re-name"`
	Model   string `xml:"software-information>product-model"`
	Type    string `xml:"software-information>package-information>name"`
	Version string `xml:"software-information>package-information>comment"`
}

// NewSession establishes a new connection to a Junos device that we will use
// to run our commands against.
func NewSession(host, user, password string) *Junos {
	s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
	if err != nil {
		log.Fatal(err)
	}

	return &Junos{
		s,
	}
}

// Commit commits the configuration.
func (j *Junos) Commit() error {
	reply, err := j.Exec(rpcCommand["commit"])
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Lock locks the candidate configuration.
func (j *Junos) Lock() error {
	reply, err := j.Exec(rpcCommand["lock"])
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Unlock unlocks the candidate configuration.
func (j *Junos) Unlock() error {
	resp, err := j.Exec(rpcCommand["unlock"])
	if err != nil {
		return err
	}

	if resp.Ok == false {
		for _, m := range resp.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// LoadConfig loads a given configuration file locally or from
// an FTP or HTTP server. Format is either "set" "text" or "xml."
func (j *Junos) LoadConfig(path, format string) error {
	var command string
	switch format {
	case "set":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-set"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-set"], string(data))
		}
	case "text":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-text"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-text"], string(data))
		}
	case "xml":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-xml"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-xml"], string(data))
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackConfig loads and commits the configuration of a given rollback or rescue state.
func (j *Junos) RollbackConfig(option interface{}) error {
	var command string
	switch option.(type) {
	case int:
		command = fmt.Sprintf(rpcCommand["rollback-config"], option)
	case string:
		command = fmt.Sprintf(rpcCommand["rescue-config"])
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	err = j.Commit()
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackDiff compares the current active configuration to a given rollback configuration.
func (j *Junos) RollbackDiff(compare int) (string, error) {
	rb := &rollbackXML{}
	command := fmt.Sprintf(rpcCommand["get-rollback-information-compare"], compare)
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), rb)
	if err != nil {
		return "", err
	}

	return rb.Config, nil
}

// Command runs any operational mode command, such as "show" or "request."
// Format is either "text" or "xml".
func (j *Junos) Command(cmd, format string) (string, error) {
	c := &commandXML{}
	var command string

	switch format {
	case "xml":
		command = fmt.Sprintf(rpcCommand["command-xml"], cmd)
	default:
		command = fmt.Sprintf(rpcCommand["command"], cmd)
	}
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &c)
	if err != nil {
		return "", err
	}

	if c.Config == "" {
		return "No output available.", nil
	}

	return c.Config, nil
}

// Close disconnects our session to the device.
func (j *Junos) Close() {
	j.Transport.Close()
}

// Software displays basic information about the device, such as software, hardware, etc.
func (j *Junos) Software() (*software, error) {
	data := &software{}
	reply, err := j.Exec(rpcCommand["software"])
	if err != nil {
		return nil, err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return nil, errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &data)
	if err != nil {
		return nil, err
	}

	return data, nil
}
<commit_msg>Added more validation for rolling back to a rescue config<commit_after>package junos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"io/ioutil"
	"log"
	"strings"
)

// Junos holds the connection information to our Junos device.
type Junos struct {
	*netconf.Session
}

// rollbackXML parses our rollback diff configuration.
type rollbackXML struct {
	XMLName xml.Name `xml:"rollback-information"`
	Config  string   `xml:"configuration-information>configuration-output"`
}

// CommandXML parses our operational command responses.
type commandXML struct {
	Config string `xml:",innerxml"`
}

type software struct {
	RES []routingEngine `xml:"multi-routing-engine-item"`
}

type routingEngine struct {
	Name    string `xml:"re-name"`
	Model   string `xml:"software-information>product-model"`
	Type    string `xml:"software-information>package-information>name"`
	Version string `xml:"software-information>package-information>comment"`
}

// NewSession establishes a new connection to a Junos device that we will use
// to run our commands against.
func NewSession(host, user, password string) *Junos {
	s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
	if err != nil {
		log.Fatal(err)
	}

	return &Junos{
		s,
	}
}

// Commit commits the configuration.
func (j *Junos) Commit() error {
	reply, err := j.Exec(rpcCommand["commit"])
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Lock locks the candidate configuration.
func (j *Junos) Lock() error {
	reply, err := j.Exec(rpcCommand["lock"])
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Unlock unlocks the candidate configuration.
func (j *Junos) Unlock() error {
	resp, err := j.Exec(rpcCommand["unlock"])
	if err != nil {
		return err
	}

	if resp.Ok == false {
		for _, m := range resp.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// LoadConfig loads a given configuration file locally or from
// an FTP or HTTP server. Format is either "set" "text" or "xml."
func (j *Junos) LoadConfig(path, format string) error {
	var command string
	switch format {
	case "set":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-set"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-set"], string(data))
		}
	case "text":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-text"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-text"], string(data))
		}
	case "xml":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-xml"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-xml"], string(data))
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackConfig loads and commits the configuration of a given rollback or rescue state.
func (j *Junos) RollbackConfig(option interface{}) error {
	var command string
	switch option.(type) {
	case int:
		command = fmt.Sprintf(rpcCommand["rollback-config"], option)
	case string:
        if option == "rescue" {
            command = fmt.Sprintf(rpcCommand["rescue-config"])
        }
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	err = j.Commit()
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackDiff compares the current active configuration to a given rollback configuration.
func (j *Junos) RollbackDiff(compare int) (string, error) {
	rb := &rollbackXML{}
	command := fmt.Sprintf(rpcCommand["get-rollback-information-compare"], compare)
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), rb)
	if err != nil {
		return "", err
	}

	return rb.Config, nil
}

// Command runs any operational mode command, such as "show" or "request."
// Format is either "text" or "xml".
func (j *Junos) Command(cmd, format string) (string, error) {
	c := &commandXML{}
	var command string

	switch format {
	case "xml":
		command = fmt.Sprintf(rpcCommand["command-xml"], cmd)
	default:
		command = fmt.Sprintf(rpcCommand["command"], cmd)
	}
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &c)
	if err != nil {
		return "", err
	}

	if c.Config == "" {
		return "No output available.", nil
	}

	return c.Config, nil
}

// Close disconnects our session to the device.
func (j *Junos) Close() {
	j.Transport.Close()
}

// Software displays basic information about the device, such as software, hardware, etc.
func (j *Junos) Software() (*software, error) {
	data := &software{}
	reply, err := j.Exec(rpcCommand["software"])
	if err != nil {
		return nil, err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return nil, errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &data)
	if err != nil {
		return nil, err
	}

	return data, nil
}
<|endoftext|>