<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	lab "github.com/fredhsu/aristalabstatus"
	"github.com/fredhsu/eapigo"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

type EosNode struct {
	Hostname      string
	ModelName     string
	MgmtIp        string
	Username      string
	Password      string
	Ssl           bool
	Reachable     bool
	ConfigCorrect bool
	Uptime        float64
	Version       string
	Config        string
	IntfConnected []string
	IpIntf        []string
	Vlans         []string
	LldpNeighbors []eapi.LldpNeighbor
}

type DemoStatus struct {
	Working bool
	Error   string
}

type ChanResponse struct {
	response eapi.JsonRpcResponse
	node     EosNode
}

type Link struct {
	Source   int `json:"source"`
	Target   int `json:"target"`
	Value    int `json:"value"`
	Distance int `json:"distance"`
}

type TopoData struct {
	Nodes []EosNode
	Links []Link
}

func writeConfigFile(path string, n EosNode, config string) {
	filename := path + n.Hostname + ".eos"
	err := ioutil.WriteFile(filename, []byte(config), 0644)
	if err != nil {
		panic(err)
	}
	fmt.Println("wrote to ", filename)
}

func readSwitches(filename string) []EosNode {
	var switches []EosNode

	file, err := os.Open("switches.json")
	if err != nil {
		panic(err)
	}
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&switches)
	if err != nil {
		panic(err)
	}
	return switches
}

func genSwitches(nodes []EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for _, node := range nodes {
			out <- node
		}
		close(out)
	}()
	return out
}

func buildUrl(node EosNode) string {
	prefix := "http"
	if node.Ssl == true {
		prefix = prefix + "s"
	}
	url := prefix + "://" + node.Username + ":" + node.Password + "@" + node.Hostname + "/command-api"
	return url
}

func getConfigs(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"enable", "show running-config"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "text")
			config := response.Result[1]["output"].(string)
			n.Config = config
			out <- n
		}
		close(out)
	}()
	return out
}

func getVersion(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"show version"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "json")
			result := response.Result[0]
			n.Version = result["version"].(string)
			n.ModelName = result["modelName"].(string)
			out <- n
		}
		close(out)
	}()
	return out
}

func getIntfConnected(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"show interfaces status connected"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "json")
			statuses := response.Result[0]["interfaceStatuses"].(map[string]interface{})
			for status := range statuses {
				n.IntfConnected = append(n.IntfConnected, status)
			}
			out <- n
		}
		close(out)
	}()
	return out
}

func getIpInterfaces(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"show ip interface"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "json")
			intfs := response.Result[0]["interfaces"].(map[string]interface{})
			for intf := range intfs {
				n.IntfConnected = append(n.IntfConnected, intf)
			}
			out <- n
		}
		close(out)
	}()
	return out
}

// Gets LLDP Neighbors and updates the EosNodes with the neighbors
func getLldpNeighbors(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			fmt.Println("getLldpNeighbors" + n.Hostname)
			cmds := []string{"show lldp neighbors"}
			data := eapi.RawCall(buildUrl(n), cmds, "json")
			// data := eapi.Call(buildUrl(n), cmds, "json")
			var jsonresp eapi.RawJsonRpcResponse
			// var jsonresp map[string]interface{}
			err := json.Unmarshal(data, &jsonresp)
			if err != nil {
				fmt.Print("Json error: ")
				fmt.Println(err)
			}
			// v := jsonresp.Result[0].(eapi.ShowLldpNeighbors)
			var v eapi.ShowLldpNeighbors
			// var jsonresp2 []json.RawMessage

			json.Unmarshal(jsonresp.Result[0], &v)
			// v := data.Result[0].(eapi.ShowLldpNeighbors)
			// fmt.Println(jsonresp2)
			// fmt.Println(jsonresp["result"])
			// fmt.Println(v)
			n.LldpNeighbors = v.LldpNeighbors
			// neighbors := (response.Result[0]["lldpNeighbors"]).([]interface{})
			// for _, neigh := range neighbors {
			//     i := neigh.(map[string]interface {})
			//     n.LldpNeighbors = append(n.LldpNeighbors, i["neighborDevice"].(string))
			// }
			out <- n
		}
		close(out)
	}()
	return out
}

// HTTP Handler for /switches
func switchesHandler(w http.ResponseWriter, r *http.Request, switches []EosNode) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
        w.Header().Set("Access-Control-Allow-Headers",
            "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

    // Stop here if its Preflighted OPTIONS request
    if r.Method == "OPTIONS" {
        return
    }

    c1 := genSwitches(switches)
	c2 := getVersion(c1)
	c2 = getLldpNeighbors(c2)
	// c2 = getIntfConnected(c2)
	// c2 = getIpInterfaces(c2)
	output := []EosNode{}
	for i := 0; i < len(switches); i++ {
		node := <-c2
		fmt.Println(node)
		output = append(output, node)
	}

	b, err := json.Marshal(output)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Fprintf(w, string(b))
}

// Used to remove the FQDN to make names shorter and consistent
func removeFqdn(host string, domain string) string {
	return strings.TrimSuffix(host, "."+domain)
}

func topoHandler(w http.ResponseWriter, r *http.Request, switches []EosNode) {
	c1 := genSwitches(switches)
	c2 := getLldpNeighbors(c1)
	nodes := []EosNode{}
	// {"source":0,"target":4,"value":1,"distance":5},
	sourceIds := map[string]int{}
	var links []Link
	for i := 0; i < len(switches); i++ {
		node := <-c2
		fmt.Println(node)
		nodes = append(nodes, node)
		sourceIds[node.Hostname] = i
		fmt.Println("sourceIds:")
		fmt.Println(sourceIds)
	}
	for i, node := range nodes {
		for _, l := range node.LldpNeighbors {
			target, ok := sourceIds[removeFqdn(l.NeighborDevice, "aristanetworks.com")]
			if !ok {
				fmt.Println("Not a valid neighbor: " + l.NeighborDevice)
			} else {
				fmt.Println("Link from " + node.Hostname + " to " + l.NeighborDevice)
				link := Link{
					Source:   i,
					Target:   target,
					Value:    1,
					Distance: 5,
				}
				// link["source"] = i
				// link["target"] = target
				// link["value"] = 1
				// link["distance"] = 5
				// fmt.Println(removeFqdn(l.NeighborDevice, "aristanetworks.com"))
				links = append(links, link)
			}
		}
	}
	output := TopoData{Nodes: nodes, Links: links}

	// nodes := string
	b, err := json.Marshal(output)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Fprintf(w, string(b))
}

func panWebHandler(w http.ResponseWriter, r *http.Request) {
    svr := "172.22.28.143:8090"
    path := "/showinterfaces"
    url := "http://" + svr + path
    res, err := http.Get(url)
    var webStatus = DemoStatus{}
    if err != nil {
        webStatus.Working = false
        webStatus.Error = err.Error()
    } else {
        webStatus.Working = true
        defer res.Body.Close()
    }
    j, err := json.Marshal([]DemoStatus{webStatus})
    if err != nil {
        fmt.Println(err)
    }
    fmt.Fprintf(w, string(j))
}

func panHandler(w http.ResponseWriter, r *http.Request) {
	backupHost := "172.22.28.27"
	dosHost := "172.22.28.28"
	// Test if panview server is up
    // Test showinterfaces

	lab.PanResume()
	lab.PanClear()
	go lab.PingHost(dosHost)
	go lab.PingHost(backupHost)
	time.Sleep(30 * time.Second)

	// check flow entries
	bypassResult, bypassReason := lab.PanFlowTest("BYPASS")
	dropResult, dropReason := lab.PanFlowTest("DROP")
	// result := bypassResult + "\n" + dropResult
	lab.PanClear()
	// Change this to send a JSON reply instead
	bypassStatus := DemoStatus{Working: bypassResult, Error: bypassReason}
	dropStatus := DemoStatus{Working: dropResult, Error: dropReason}
	j, err := json.Marshal([]DemoStatus{bypassStatus, dropStatus})
	if err != nil {
		fmt.Fprintf(w, "Error encoding PAN demo response")
		return
	}
	fmt.Fprintf(w, string(j))
}

func dashViewHandler(w http.ResponseWriter, r *http.Request) {
	tmpl_list := []string{"../templates/base.html", "../templates/navbar.html", "../templates/header.html", "../templates/footer.html"}
	t, err := template.ParseFiles(tmpl_list...)
	if err != nil {
		fmt.Print("template parsing error: ", err)
	}
	err = t.Execute(w, nil)
}

func main() {
	swFilePtr := flag.String("swfile", "switches.json", "A JSON file with switches to fetch")
	flag.Parse() // command-line flag parsing
	switches := readSwitches(*swFilePtr)

	http.HandleFunc("/switches", func(w http.ResponseWriter, r *http.Request) {
		switchesHandler(w, r, switches)
	})
	http.HandleFunc("/topo", func(w http.ResponseWriter, r *http.Request) {
		topoHandler(w, r, switches)
	})
	http.HandleFunc("/pan", func(w http.ResponseWriter, r *http.Request) {
		panHandler(w, r)
	})
    http.HandleFunc("/panweb", func(w http.ResponseWriter, r *http.Request) {
        panWebHandler(w, r)
    })

	http.HandleFunc("/view/dashboard", func(w http.ResponseWriter, r *http.Request) {
		dashViewHandler(w, r)
	})

	http.ListenAndServe(":8081", nil)
}
<commit_msg>Added wrapper for CORS handling<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	lab "github.com/fredhsu/aristalabstatus"
	"github.com/fredhsu/eapigo"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

type EosNode struct {
	Hostname      string
	ModelName     string
	MgmtIp        string
	Username      string
	Password      string
	Ssl           bool
	Reachable     bool
	ConfigCorrect bool
	Uptime        float64
	Version       string
	Config        string
	IntfConnected []string
	IpIntf        []string
	Vlans         []string
	LldpNeighbors []eapi.LldpNeighbor
}

type DemoStatus struct {
	Working bool
	Error   string
}

type ChanResponse struct {
	response eapi.JsonRpcResponse
	node     EosNode
}

type Link struct {
	Source   int `json:"source"`
	Target   int `json:"target"`
	Value    int `json:"value"`
	Distance int `json:"distance"`
}

type TopoData struct {
	Nodes []EosNode
	Links []Link
}

func writeConfigFile(path string, n EosNode, config string) {
	filename := path + n.Hostname + ".eos"
	err := ioutil.WriteFile(filename, []byte(config), 0644)
	if err != nil {
		panic(err)
	}
	fmt.Println("wrote to ", filename)
}

func readSwitches(filename string) []EosNode {
	var switches []EosNode

	file, err := os.Open("switches.json")
	if err != nil {
		panic(err)
	}
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&switches)
	if err != nil {
		panic(err)
	}
	return switches
}

func genSwitches(nodes []EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for _, node := range nodes {
			out <- node
		}
		close(out)
	}()
	return out
}

func buildUrl(node EosNode) string {
	prefix := "http"
	if node.Ssl == true {
		prefix = prefix + "s"
	}
	url := prefix + "://" + node.Username + ":" + node.Password + "@" + node.Hostname + "/command-api"
	return url
}

func getConfigs(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"enable", "show running-config"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "text")
			config := response.Result[1]["output"].(string)
			n.Config = config
			out <- n
		}
		close(out)
	}()
	return out
}

func getVersion(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"show version"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "json")
			result := response.Result[0]
			n.Version = result["version"].(string)
			n.ModelName = result["modelName"].(string)
			out <- n
		}
		close(out)
	}()
	return out
}

func getIntfConnected(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"show interfaces status connected"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "json")
			statuses := response.Result[0]["interfaceStatuses"].(map[string]interface{})
			for status := range statuses {
				n.IntfConnected = append(n.IntfConnected, status)
			}
			out <- n
		}
		close(out)
	}()
	return out
}

func getIpInterfaces(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			cmds := []string{"show ip interface"}
			url := buildUrl(n)
			response := eapi.Call(url, cmds, "json")
			intfs := response.Result[0]["interfaces"].(map[string]interface{})
			for intf := range intfs {
				n.IntfConnected = append(n.IntfConnected, intf)
			}
			out <- n
		}
		close(out)
	}()
	return out
}

// Gets LLDP Neighbors and updates the EosNodes with the neighbors
func getLldpNeighbors(in <-chan EosNode) <-chan EosNode {
	out := make(chan EosNode)
	go func() {
		for n := range in {
			fmt.Println("getLldpNeighbors" + n.Hostname)
			cmds := []string{"show lldp neighbors"}
			data := eapi.RawCall(buildUrl(n), cmds, "json")
			// data := eapi.Call(buildUrl(n), cmds, "json")
			var jsonresp eapi.RawJsonRpcResponse
			// var jsonresp map[string]interface{}
			err := json.Unmarshal(data, &jsonresp)
			if err != nil {
				fmt.Print("Json error: ")
				fmt.Println(err)
			}
			// v := jsonresp.Result[0].(eapi.ShowLldpNeighbors)
			var v eapi.ShowLldpNeighbors
			// var jsonresp2 []json.RawMessage

			json.Unmarshal(jsonresp.Result[0], &v)
			// v := data.Result[0].(eapi.ShowLldpNeighbors)
			// fmt.Println(jsonresp2)
			// fmt.Println(jsonresp["result"])
			// fmt.Println(v)
			n.LldpNeighbors = v.LldpNeighbors
			// neighbors := (response.Result[0]["lldpNeighbors"]).([]interface{})
			// for _, neigh := range neighbors {
			//     i := neigh.(map[string]interface {})
			//     n.LldpNeighbors = append(n.LldpNeighbors, i["neighborDevice"].(string))
			// }
			out <- n
		}
		close(out)
	}()
	return out
}

// HTTP Handler for /switches
func switchesHandler(w http.ResponseWriter, r *http.Request, switches []EosNode) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	w.Header().Set("Access-Control-Allow-Headers",
		"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")

	// Stop here if its Preflighted OPTIONS request
	if r.Method == "OPTIONS" {
		return
	}

	c1 := genSwitches(switches)
	c2 := getVersion(c1)
	c2 = getLldpNeighbors(c2)
	// c2 = getIntfConnected(c2)
	// c2 = getIpInterfaces(c2)
	output := []EosNode{}
	for i := 0; i < len(switches); i++ {
		node := <-c2
		fmt.Println(node)
		output = append(output, node)
	}

	b, err := json.Marshal(output)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Fprintf(w, string(b))
}

// Used to remove the FQDN to make names shorter and consistent
func removeFqdn(host string, domain string) string {
	return strings.TrimSuffix(host, "."+domain)
}

func topoHandler(w http.ResponseWriter, r *http.Request, switches []EosNode) {
	c1 := genSwitches(switches)
	c2 := getLldpNeighbors(c1)
	nodes := []EosNode{}
	// {"source":0,"target":4,"value":1,"distance":5},
	sourceIds := map[string]int{}
	var links []Link
	for i := 0; i < len(switches); i++ {
		node := <-c2
		fmt.Println(node)
		nodes = append(nodes, node)
		sourceIds[node.Hostname] = i
		fmt.Println("sourceIds:")
		fmt.Println(sourceIds)
	}
	for i, node := range nodes {
		for _, l := range node.LldpNeighbors {
			target, ok := sourceIds[removeFqdn(l.NeighborDevice, "aristanetworks.com")]
			if !ok {
				fmt.Println("Not a valid neighbor: " + l.NeighborDevice)
			} else {
				fmt.Println("Link from " + node.Hostname + " to " + l.NeighborDevice)
				link := Link{
					Source:   i,
					Target:   target,
					Value:    1,
					Distance: 5,
				}
				// link["source"] = i
				// link["target"] = target
				// link["value"] = 1
				// link["distance"] = 5
				// fmt.Println(removeFqdn(l.NeighborDevice, "aristanetworks.com"))
				links = append(links, link)
			}
		}
	}
	output := TopoData{Nodes: nodes, Links: links}

	// nodes := string
	b, err := json.Marshal(output)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Fprintf(w, string(b))
}

func panWebHandler(w http.ResponseWriter, r *http.Request, switches []EosNode) {
	svr := "172.22.28.143:8090"
	path := "/showinterfaces"
	url := "http://" + svr + path
	res, err := http.Get(url)
	var webStatus = DemoStatus{}
	if err != nil {
		webStatus.Working = false
		webStatus.Error = err.Error()
	} else {
		webStatus.Working = true
		defer res.Body.Close()
	}
	j, err := json.Marshal([]DemoStatus{webStatus})
	if err != nil {
		fmt.Println(err)
	}
	fmt.Fprintf(w, string(j))
}

func panHandler(w http.ResponseWriter, r *http.Request, switches []EosNode) {
	backupHost := "172.22.28.27"
	dosHost := "172.22.28.28"
	// Test if panview server is up
	// Test showinterfaces

	lab.PanResume()
	lab.PanClear()
	go lab.PingHost(dosHost)
	go lab.PingHost(backupHost)
	time.Sleep(30 * time.Second)

	// check flow entries
	bypassResult, bypassReason := lab.PanFlowTest("BYPASS")
	dropResult, dropReason := lab.PanFlowTest("DROP")
	// result := bypassResult + "\n" + dropResult
	lab.PanClear()
	// Change this to send a JSON reply instead
	bypassStatus := DemoStatus{Working: bypassResult, Error: bypassReason}
	dropStatus := DemoStatus{Working: dropResult, Error: dropReason}
	j, err := json.Marshal([]DemoStatus{bypassStatus, dropStatus})
	if err != nil {
		fmt.Fprintf(w, "Error encoding PAN demo response")
		return
	}
	fmt.Fprintf(w, string(j))
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, []EosNode), switches []EosNode) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
		// Stop here if its Preflighted OPTIONS request
		if r.Method == "OPTIONS" {
			return
		}
		fn(w, r, switches)
	}
}

func main() {
	swFilePtr := flag.String("swfile", "switches.json", "A JSON file with switches to fetch")
	flag.Parse() // command-line flag parsing
	switches := readSwitches(*swFilePtr)

	// http.HandleFunc("/switches", func(w http.ResponseWriter, r *http.Request) {
	// 	switchesHandler(w, r, switches)
	// })
	// http.HandleFunc("/topo", func(w http.ResponseWriter, r *http.Request) {
	// 	topoHandler(w, r, switches)
	// })
	// http.HandleFunc("/pan", func(w http.ResponseWriter, r *http.Request) {
	// 	panHandler(w, r, switches)
	// })
	http.HandleFunc("/switches", makeHandler(switchesHandler, switches))
	http.HandleFunc("/topo", makeHandler(topoHandler, switches))
	http.HandleFunc("/pan", makeHandler(panHandler, switches))
	// http.HandleFunc("/panweb", func(w http.ResponseWriter, r *http.Request) {
	// 	panWebHandler(w, r)
	// })
	http.HandleFunc("/panweb", makeHandler(panWebHandler, switches))

	http.ListenAndServe(":8081", nil)
}
<|endoftext|>


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

// The ct_server binary runs the CT personality.
package main

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/etcd/clientv3"
	etcdnaming "github.com/coreos/etcd/clientv3/naming"
	"github.com/golang/glog"
	"github.com/google/certificate-transparency-go/trillian/ctfe"
	"github.com/google/certificate-transparency-go/trillian/util"
	"github.com/google/trillian"
	"github.com/google/trillian/crypto/keys"
	"github.com/google/trillian/monitoring/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc"
	"google.golang.org/grpc/naming"
)

// Global flags that affect all log instances.
var (
	httpEndpoint       = flag.String("http_endpoint", "localhost:6962", "Endpoint for HTTP (host:port)")
	metricsEndpoint    = flag.String("metrics_endpoint", "localhost:6963", "Endpoint for serving metrics; if left empty, metrics will be visible on --http_endpoint")
	rpcBackendFlag     = flag.String("log_rpc_server", "localhost:8090", "Backend specification; comma-separated list or etcd service name (if --etcd_servers specified)")
	rpcDeadlineFlag    = flag.Duration("rpc_deadline", time.Second*10, "Deadline for backend RPC requests")
	logConfigFlag      = flag.String("log_config", "", "File holding log config in JSON")
	maxGetEntriesFlag  = flag.Int64("maxGetEntriesAllowed", 0, "Max number of entries we allow in a get-entries request (default 50)")
	etcdServers        = flag.String("etcd_servers", "", "A comma-separated list of etcd servers")
	etcdHTTPService    = flag.String("etcd_http_service", "trillian-ctfe-http", "Service name to announce our HTTP endpoint under")
	etcdMetricsService = flag.String("etcd_metrics_service", "trillian-ctfe-metrics-http", "Service name to announce our HTTP metrics endpoint under")
)

func main() {
	flag.Parse()
	ctx := context.Background()

	if *maxGetEntriesFlag > 0 {
		ctfe.MaxGetEntriesAllowed = *maxGetEntriesFlag
	}

	// Get log config from file before we start.
	cfg, err := ctfe.LogConfigFromFile(*logConfigFlag)
	if err != nil {
		glog.Exitf("Failed to read log config: %v", err)
	}

	glog.CopyStandardLogTo("WARNING")
	glog.Info("**** CT HTTP Server Starting ****")

	metricsAt := *metricsEndpoint
	if metricsAt == "" {
		metricsAt = *httpEndpoint
	}

	// TODO(Martin2112): Support TLS and other stuff for RPC client and http server, this is just to
	// get started. Uses a blocking connection so we don't start serving before we're connected
	// to backend.
	var res naming.Resolver
	if len(*etcdServers) > 0 {
		// Use etcd to provide endpoint resolution.
		cfg := clientv3.Config{Endpoints: strings.Split(*etcdServers, ","), DialTimeout: 5 * time.Second}
		client, err := clientv3.New(cfg)
		if err != nil {
			glog.Exitf("Failed to connect to etcd at %v: %v", *etcdServers, err)
		}
		etcdRes := &etcdnaming.GRPCResolver{Client: client}
		res = etcdRes

		// Also announce ourselves.
		updateHTTP := naming.Update{Op: naming.Add, Addr: *httpEndpoint}
		updateMetrics := naming.Update{Op: naming.Add, Addr: metricsAt}
		glog.Infof("Announcing our presence in %v with %+v", *etcdHTTPService, updateHTTP)
		etcdRes.Update(ctx, *etcdHTTPService, updateHTTP)
		glog.Infof("Announcing our presence in %v with %+v", *etcdMetricsService, updateMetrics)
		etcdRes.Update(ctx, *etcdMetricsService, updateMetrics)

		byeHTTP := naming.Update{Op: naming.Delete, Addr: *httpEndpoint}
		byeMetrics := naming.Update{Op: naming.Delete, Addr: metricsAt}
		defer func() {
			glog.Infof("Removing our presence in %v with %+v", *etcdHTTPService, byeHTTP)
			etcdRes.Update(ctx, *etcdHTTPService, byeHTTP)
			glog.Infof("Removing our presence in %v with %+v", *etcdMetricsService, byeMetrics)
			etcdRes.Update(ctx, *etcdMetricsService, byeMetrics)
		}()
	} else {
		// Use a fixed endpoint resolution that just returns the addresses configured on the command line.
		res = util.FixedBackendResolver{}
	}
	bal := grpc.RoundRobin(res)
	conn, err := grpc.Dial(*rpcBackendFlag, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithBalancer(bal))
	if err != nil {
		glog.Exitf("Could not connect to rpc server: %v", err)
	}
	defer conn.Close()
	client := trillian.NewTrillianLogClient(conn)

	sf := &keys.DefaultSignerFactory{}

	for _, c := range cfg {
		handlers, err := ctfe.SetUpInstance(ctx, client, c, sf, *rpcDeadlineFlag, prometheus.MetricFactory{})
		if err != nil {
			glog.Exitf("Failed to set up log instance for %+v: %v", cfg, err)
		}
		for path, handler := range *handlers {
			http.Handle(path, handler)
		}
	}
	if metricsAt != *httpEndpoint {
		// Run a separate handler for metrics.
		go func() {
			mux := http.NewServeMux()
			mux.Handle("/metrics", promhttp.Handler())
			metricsServer := http.Server{Addr: metricsAt, Handler: mux}
			err := metricsServer.ListenAndServe()
			glog.Warningf("Metrics server exited: %v", err)
		}()
	} else {
		// Handle metrics on the DefaultServeMux.
		http.Handle("/metrics", promhttp.Handler())
	}

	// Bring up the HTTP server and serve until we get a signal not to.
	go awaitSignal(func() {
		os.Exit(1)
	})
	server := http.Server{Addr: *httpEndpoint, Handler: nil}
	err = server.ListenAndServe()
	glog.Warningf("Server exited: %v", err)
	glog.Flush()
}

// awaitSignal waits for standard termination signals, then runs the given
// function; it should be run as a separate goroutine.
func awaitSignal(doneFn func()) {
	// Arrange notification for the standard set of signals used to terminate a server
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// Now block main and wait for a signal
	sig := <-sigs
	glog.Warningf("Signal received: %v", sig)
	glog.Flush()

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

// The ct_server binary runs the CT personality.
package main

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/etcd/clientv3"
	etcdnaming "github.com/coreos/etcd/clientv3/naming"
	"github.com/golang/glog"
	"github.com/google/certificate-transparency-go/trillian/ctfe"
	"github.com/google/certificate-transparency-go/trillian/util"
	"github.com/google/trillian"
	"github.com/google/trillian/crypto/keys"
	"github.com/google/trillian/monitoring/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc"
	"google.golang.org/grpc/naming"
)

// Global flags that affect all log instances.
var (
	httpEndpoint       = flag.String("http_endpoint", "localhost:6962", "Endpoint for HTTP (host:port)")
	metricsEndpoint    = flag.String("metrics_endpoint", "localhost:6963", "Endpoint for serving metrics; if left empty, metrics will be visible on --http_endpoint")
	rpcBackendFlag     = flag.String("log_rpc_server", "localhost:8090", "Backend specification; comma-separated list or etcd service name (if --etcd_servers specified)")
	rpcDeadlineFlag    = flag.Duration("rpc_deadline", time.Second*10, "Deadline for backend RPC requests")
	logConfigFlag      = flag.String("log_config", "", "File holding log config in JSON")
	maxGetEntriesFlag  = flag.Int64("max_get_entries", 0, "Max number of entries we allow in a get-entries request (0=>use default 50)")
	etcdServers        = flag.String("etcd_servers", "", "A comma-separated list of etcd servers")
	etcdHTTPService    = flag.String("etcd_http_service", "trillian-ctfe-http", "Service name to announce our HTTP endpoint under")
	etcdMetricsService = flag.String("etcd_metrics_service", "trillian-ctfe-metrics-http", "Service name to announce our HTTP metrics endpoint under")
)

func main() {
	flag.Parse()
	ctx := context.Background()

	if *maxGetEntriesFlag > 0 {
		ctfe.MaxGetEntriesAllowed = *maxGetEntriesFlag
	}

	// Get log config from file before we start.
	cfg, err := ctfe.LogConfigFromFile(*logConfigFlag)
	if err != nil {
		glog.Exitf("Failed to read log config: %v", err)
	}

	glog.CopyStandardLogTo("WARNING")
	glog.Info("**** CT HTTP Server Starting ****")

	metricsAt := *metricsEndpoint
	if metricsAt == "" {
		metricsAt = *httpEndpoint
	}

	// TODO(Martin2112): Support TLS and other stuff for RPC client and http server, this is just to
	// get started. Uses a blocking connection so we don't start serving before we're connected
	// to backend.
	var res naming.Resolver
	if len(*etcdServers) > 0 {
		// Use etcd to provide endpoint resolution.
		cfg := clientv3.Config{Endpoints: strings.Split(*etcdServers, ","), DialTimeout: 5 * time.Second}
		client, err := clientv3.New(cfg)
		if err != nil {
			glog.Exitf("Failed to connect to etcd at %v: %v", *etcdServers, err)
		}
		etcdRes := &etcdnaming.GRPCResolver{Client: client}
		res = etcdRes

		// Also announce ourselves.
		updateHTTP := naming.Update{Op: naming.Add, Addr: *httpEndpoint}
		updateMetrics := naming.Update{Op: naming.Add, Addr: metricsAt}
		glog.Infof("Announcing our presence in %v with %+v", *etcdHTTPService, updateHTTP)
		etcdRes.Update(ctx, *etcdHTTPService, updateHTTP)
		glog.Infof("Announcing our presence in %v with %+v", *etcdMetricsService, updateMetrics)
		etcdRes.Update(ctx, *etcdMetricsService, updateMetrics)

		byeHTTP := naming.Update{Op: naming.Delete, Addr: *httpEndpoint}
		byeMetrics := naming.Update{Op: naming.Delete, Addr: metricsAt}
		defer func() {
			glog.Infof("Removing our presence in %v with %+v", *etcdHTTPService, byeHTTP)
			etcdRes.Update(ctx, *etcdHTTPService, byeHTTP)
			glog.Infof("Removing our presence in %v with %+v", *etcdMetricsService, byeMetrics)
			etcdRes.Update(ctx, *etcdMetricsService, byeMetrics)
		}()
	} else {
		// Use a fixed endpoint resolution that just returns the addresses configured on the command line.
		res = util.FixedBackendResolver{}
	}
	bal := grpc.RoundRobin(res)
	conn, err := grpc.Dial(*rpcBackendFlag, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithBalancer(bal))
	if err != nil {
		glog.Exitf("Could not connect to rpc server: %v", err)
	}
	defer conn.Close()
	client := trillian.NewTrillianLogClient(conn)

	sf := &keys.DefaultSignerFactory{}

	for _, c := range cfg {
		handlers, err := ctfe.SetUpInstance(ctx, client, c, sf, *rpcDeadlineFlag, prometheus.MetricFactory{})
		if err != nil {
			glog.Exitf("Failed to set up log instance for %+v: %v", cfg, err)
		}
		for path, handler := range *handlers {
			http.Handle(path, handler)
		}
	}
	if metricsAt != *httpEndpoint {
		// Run a separate handler for metrics.
		go func() {
			mux := http.NewServeMux()
			mux.Handle("/metrics", promhttp.Handler())
			metricsServer := http.Server{Addr: metricsAt, Handler: mux}
			err := metricsServer.ListenAndServe()
			glog.Warningf("Metrics server exited: %v", err)
		}()
	} else {
		// Handle metrics on the DefaultServeMux.
		http.Handle("/metrics", promhttp.Handler())
	}

	// Bring up the HTTP server and serve until we get a signal not to.
	go awaitSignal(func() {
		os.Exit(1)
	})
	server := http.Server{Addr: *httpEndpoint, Handler: nil}
	err = server.ListenAndServe()
	glog.Warningf("Server exited: %v", err)
	glog.Flush()
}

// awaitSignal waits for standard termination signals, then runs the given
// function; it should be run as a separate goroutine.
func awaitSignal(doneFn func()) {
	// Arrange notification for the standard set of signals used to terminate a server
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// Now block main and wait for a signal
	sig := <-sigs
	glog.Warningf("Signal received: %v", sig)
	glog.Flush()

	doneFn()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/xyproto/permissions2"
	"github.com/xyproto/pinterface"
)

type permissionHandler struct {
	// perm is a Permissions structure that can be used to deny requests
	// and aquire the UserState. By using `pinterface.IPermissions` instead
	// of `permissions.Permissions`, the code is compatible with not only
	// `permissions2`, but also other modules that uses other database
	// backends, like `permissionbolt` which uses Bolt.
	perm pinterface.IPermissions

	// The HTTP multiplexer
	mux *http.ServeMux
}

// Implement the ServeHTTP method to make a permissionHandler a http.Handler
func (ph *permissionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Check if the user has the right admin/user rights
	if ph.perm.Rejected(w, req) {
		// Let the user know, by calling the custom "permission denied" function
		ph.perm.DenyFunction()(w, req)
		// Reject the request by not calling the next handler below
		return
	}
	// Serve the requested page if permissions were granted
	ph.mux.ServeHTTP(w, req)
}

func main() {
	mux := http.NewServeMux()

	// New permissions middleware
	perm := permissions.New()

	// Blank slate, no default permissions
	//perm.Clear()

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Has user bob: %v\n", userstate.HasUser("bob"))
		fmt.Fprintf(w, "Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
		fmt.Fprintf(w, "Is confirmed: %v\n", userstate.IsConfirmed("bob"))
		fmt.Fprintf(w, "Username stored in cookies (or blank): %v\n", userstate.Username(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(req))
		fmt.Fprintf(w, "\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
	})

	mux.HandleFunc("/register", func(w http.ResponseWriter, req *http.Request) {
		userstate.AddUser("bob", "hunter1", "bob@zombo.com")
		fmt.Fprintf(w, "User bob was created: %v\n", userstate.HasUser("bob"))
	})

	mux.HandleFunc("/confirm", func(w http.ResponseWriter, req *http.Request) {
		userstate.MarkConfirmed("bob")
		fmt.Fprintf(w, "User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
	})

	mux.HandleFunc("/remove", func(w http.ResponseWriter, req *http.Request) {
		userstate.RemoveUser("bob")
		fmt.Fprintf(w, "User bob was removed: %v\n", !userstate.HasUser("bob"))
	})

	mux.HandleFunc("/login", func(w http.ResponseWriter, req *http.Request) {
		userstate.Login(w, "bob")
		fmt.Fprintf(w, "bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
	})

	mux.HandleFunc("/logout", func(w http.ResponseWriter, req *http.Request) {
		userstate.Logout("bob")
		fmt.Fprintf(w, "bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
	})

	mux.HandleFunc("/makeadmin", func(w http.ResponseWriter, req *http.Request) {
		userstate.SetAdminStatus("bob")
		fmt.Fprintf(w, "bob is now administrator: %v\n", userstate.IsAdmin("bob"))
	})

	mux.HandleFunc("/clear", func(w http.ResponseWriter, req *http.Request) {
		userstate.ClearCookie(w)
		fmt.Fprintf(w, "Clearing cookie")
	})

	mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "user page that only logged in users must see!")
	})

	mux.HandleFunc("/admin", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "super secret information that only logged in administrators must see!\n\n")
		if usernames, err := userstate.AllUsernames(); err == nil {
			fmt.Fprintf(w, "list of all users: "+strings.Join(usernames, ", "))
		}
	})

	// Custom handler for when permissions are denied
	perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) {
		http.Error(w, "Permission denied!", http.StatusForbidden)
	})

	// Configure the HTTP server and permissionHandler struct
	s := &http.Server{
		Addr:           ":3000",
		Handler:        &permissionHandler{perm, mux},
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	log.Println("Listening for requests on port 3000")

	// Start listening
	s.ListenAndServe()
}
<commit_msg>Correct a comment in the net/http example<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/xyproto/permissions2"
	"github.com/xyproto/pinterface"
)

type permissionHandler struct {
	// perm is a Permissions structure that can be used to deny requests
	// and aquire the UserState. By using `pinterface.IPermissions` instead
	// of `permissions.Permissions`, the code is compatible with not only
	// `permissions2`, but also other modules that uses other database
	// backends, like `permissionbolt` which uses Bolt.
	perm pinterface.IPermissions

	// The HTTP multiplexer
	mux *http.ServeMux
}

// Implement the ServeHTTP method to make a permissionHandler a http.Handler
func (ph *permissionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Check if the user has the right admin/user rights
	if ph.perm.Rejected(w, req) {
		// Let the user know, by calling the custom "permission denied" function
		ph.perm.DenyFunction()(w, req)
		// Reject the request
		return
	}
	// Serve the requested page if permissions were granted
	ph.mux.ServeHTTP(w, req)
}

func main() {
	mux := http.NewServeMux()

	// New permissions middleware
	perm := permissions.New()

	// Blank slate, no default permissions
	//perm.Clear()

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Has user bob: %v\n", userstate.HasUser("bob"))
		fmt.Fprintf(w, "Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
		fmt.Fprintf(w, "Is confirmed: %v\n", userstate.IsConfirmed("bob"))
		fmt.Fprintf(w, "Username stored in cookies (or blank): %v\n", userstate.Username(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(req))
		fmt.Fprintf(w, "\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
	})

	mux.HandleFunc("/register", func(w http.ResponseWriter, req *http.Request) {
		userstate.AddUser("bob", "hunter1", "bob@zombo.com")
		fmt.Fprintf(w, "User bob was created: %v\n", userstate.HasUser("bob"))
	})

	mux.HandleFunc("/confirm", func(w http.ResponseWriter, req *http.Request) {
		userstate.MarkConfirmed("bob")
		fmt.Fprintf(w, "User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
	})

	mux.HandleFunc("/remove", func(w http.ResponseWriter, req *http.Request) {
		userstate.RemoveUser("bob")
		fmt.Fprintf(w, "User bob was removed: %v\n", !userstate.HasUser("bob"))
	})

	mux.HandleFunc("/login", func(w http.ResponseWriter, req *http.Request) {
		userstate.Login(w, "bob")
		fmt.Fprintf(w, "bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
	})

	mux.HandleFunc("/logout", func(w http.ResponseWriter, req *http.Request) {
		userstate.Logout("bob")
		fmt.Fprintf(w, "bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
	})

	mux.HandleFunc("/makeadmin", func(w http.ResponseWriter, req *http.Request) {
		userstate.SetAdminStatus("bob")
		fmt.Fprintf(w, "bob is now administrator: %v\n", userstate.IsAdmin("bob"))
	})

	mux.HandleFunc("/clear", func(w http.ResponseWriter, req *http.Request) {
		userstate.ClearCookie(w)
		fmt.Fprintf(w, "Clearing cookie")
	})

	mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "user page that only logged in users must see!")
	})

	mux.HandleFunc("/admin", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "super secret information that only logged in administrators must see!\n\n")
		if usernames, err := userstate.AllUsernames(); err == nil {
			fmt.Fprintf(w, "list of all users: "+strings.Join(usernames, ", "))
		}
	})

	// Custom handler for when permissions are denied
	perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) {
		http.Error(w, "Permission denied!", http.StatusForbidden)
	})

	// Configure the HTTP server and permissionHandler struct
	s := &http.Server{
		Addr:           ":3000",
		Handler:        &permissionHandler{perm, mux},
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	log.Println("Listening for requests on port 3000")

	// Start listening
	s.ListenAndServe()
}
<|endoftext|>


<commit_before>package performanceboard

import (
	"appengine"
	"appengine/datastore"
	"appengine/user"
	"github.com/gorilla/mux"
	"net/http"
)

const BoardKind = "Board"

type Board struct {
	Key    *datastore.Key `datastore:"-"`
	UserID string
}

func (board *Board) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	api, _ := router.Get("board").URL("board", board.Key.Encode())
	JsonResponse{
		"board": board.Key.Encode(),
		"api":   AbsURL(*api, request),
	}.Write(writer)
}

// HTTP handlers

func createBoard(writer http.ResponseWriter, request *http.Request) {
	context := appengine.NewContext(request)
	board := Board{}
	if u := user.Current(context); u != nil {
		board.UserID = u.ID
	}
	board.Key, _ = datastore.Put(context, datastore.NewIncompleteKey(context, BoardKind, nil), &board)
	board.ServeHTTP(writer, request)
}

func clearBoard(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	keyString := mux.Vars(r)["board"]
	key, err := datastore.DecodeKey(keyString)
	if err != nil || key.Kind() != BoardKind {
		http.Error(w, "Invalid board key: "+keyString, http.StatusBadRequest)
		return
	}
	board := Board{Key: key}
	if err = datastore.Get(c, key, &board); err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}
	keys := []*datastore.Key{}
	keys, err = datastore.NewQuery(MetricKind).Ancestor(key).KeysOnly().GetAll(c, nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = datastore.DeleteMulti(c, keys); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	board.ServeHTTP(w, r)
}
<commit_msg>Reworking clear board<commit_after>package performanceboard

import (
	"appengine"
	"appengine/datastore"
	"appengine/user"
	"github.com/gorilla/mux"
	"net/http"
)

const BoardKind = "Board"

type Board struct {
	Key    *datastore.Key `datastore:"-"`
	UserID string
}

func (board *Board) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	api, _ := router.Get("board").URL("board", board.Key.Encode())
	JsonResponse{
		"board": board.Key.Encode(),
		"api":   AbsURL(*api, request),
	}.Write(writer)
}

// HTTP handlers

func createBoard(writer http.ResponseWriter, request *http.Request) {
	context := appengine.NewContext(request)
	board := Board{}
	if u := user.Current(context); u != nil {
		board.UserID = u.ID
	}
	board.Key, _ = datastore.Put(context, datastore.NewIncompleteKey(context, BoardKind, nil), &board)
	board.ServeHTTP(writer, request)
}

func clearBoard(w http.ResponseWriter, r *http.Request) {
	keyString := mux.Vars(r)["board"]
	key, err := datastore.DecodeKey(keyString)
	if err != nil || key.Kind() != BoardKind {
		http.Error(w, "Invalid board key: "+keyString, http.StatusBadRequest)
		return
	}
	c := appengine.NewContext(r)
	board := Board{Key: key}
	if err = datastore.Get(c, key, &board); err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}
	keys := []*datastore.Key{}
	t := datastore.NewQuery(MetricKind).Ancestor(key).KeysOnly().Run(c)
	keyChan := make(chan *datastore.Key, 50)
	go func() {
		for {
			key, err := t.Next(nil)
			if err == datastore.Done {
				keyChan <- key
				break
			} else if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				break
			}
		}
		close(keyChan)
	}()
	errChan := make(chan error, 50)
	go func() {
		for key := range keyChan {
			go func(key *datastore.Key) {
				errChan <- datastore.Delete(c, key)
			}(key)
		}
		close(errChan)
	}()
	for err := range errChan {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	board.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package log

import (
	"io"
	"os"

	"log/syslog"

	"github.com/sirupsen/logrus"
	ls "github.com/sirupsen/logrus/hooks/syslog"
)

func New(logLevel string, logServer string, logOutput string) *logrus.Logger {
	var level logrus.Level
	var output io.Writer
	var hook logrus.Hook

	switch logOutput {
	case "stdout":
		output = os.Stdout
	case "stderr":
		output = os.Stderr
	case "syslog":
		output = os.Stderr // does not matter ?
		if logServer == "" {
			panic("syslog output needs a log server (ie. 127.0.0.1:514)")
		}
		hook, _ = ls.NewSyslogHook("udp", logServer, syslog.LOG_INFO, "kube-alert")
	default:
		output = os.Stderr
	}

	switch logLevel {
	case "debug":
		level = logrus.DebugLevel
	case "info":
		level = logrus.InfoLevel
	case "warning":
		level = logrus.WarnLevel
	case "error":
		level = logrus.ErrorLevel
	case "fatal":
		level = logrus.FatalLevel
	case "panic":
		level = logrus.PanicLevel
	default:
		level = logrus.InfoLevel
	}

	log := &logrus.Logger{
		Out:       output,
		Formatter: new(logrus.TextFormatter), //new(logrus.JSONFormatter),
		Hooks:     make(logrus.LevelHooks),
		Level:     level,
	}

	if logOutput == "syslog" {
		log.Hooks.Add(hook)
	}

	return log
}
<commit_msg>Timestamps in logs output<commit_after>package log

import (
	"io"
	"os"

	"log/syslog"

	"github.com/sirupsen/logrus"
	ls "github.com/sirupsen/logrus/hooks/syslog"
)

func New(logLevel string, logServer string, logOutput string) *logrus.Logger {
	var level logrus.Level
	var output io.Writer
	var hook logrus.Hook

	switch logOutput {
	case "stdout":
		output = os.Stdout
	case "stderr":
		output = os.Stderr
	case "syslog":
		output = os.Stderr // does not matter ?
		if logServer == "" {
			panic("syslog output needs a log server (ie. 127.0.0.1:514)")
		}
		hook, _ = ls.NewSyslogHook("udp", logServer, syslog.LOG_INFO, "kube-alert")
	default:
		output = os.Stderr
	}

	switch logLevel {
	case "debug":
		level = logrus.DebugLevel
	case "info":
		level = logrus.InfoLevel
	case "warning":
		level = logrus.WarnLevel
	case "error":
		level = logrus.ErrorLevel
	case "fatal":
		level = logrus.FatalLevel
	case "panic":
		level = logrus.PanicLevel
	default:
		level = logrus.InfoLevel
	}

	formater := &logrus.TextFormatter{
		FullTimestamp: true,
		TimestampFormat: "2006-01-02 15:04:05",
	}

	log := &logrus.Logger{
		Out:       output,
		Formatter: formater,
		Hooks:     make(logrus.LevelHooks),
		Level:     level,
	}

	if logOutput == "syslog" {
		log.Hooks.Add(hook)
	}

	return log
}
<|endoftext|>


<commit_before>//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package pci

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"

	"github.com/jaypipes/pcidb"

	"github.com/jaypipes/ghw/pkg/context"
	"github.com/jaypipes/ghw/pkg/marshal"
	"github.com/jaypipes/ghw/pkg/option"
	"github.com/jaypipes/ghw/pkg/util"
)

var (
	regexAddress *regexp.Regexp = regexp.MustCompile(
		`^(([0-9a-f]{0,4}):)?([0-9a-f]{2}):([0-9a-f]{2})\.([0-9a-f]{1})$`,
	)
)

type Device struct {
	// The PCI address of the device
	Address   string         `json:"address"`
	Vendor    *pcidb.Vendor  `json:"vendor"`
	Product   *pcidb.Product `json:"product"`
	Subsystem *pcidb.Product `json:"subsystem"`
	// optional subvendor/sub-device information
	Class *pcidb.Class `json:"class"`
	// optional sub-class for the device
	Subclass *pcidb.Subclass `json:"subclass"`
	// optional programming interface
	ProgrammingInterface *pcidb.ProgrammingInterface `json:"programming_interface"`
}

// NOTE(jaypipes) Device has a custom JSON marshaller because we don't want
// to serialize the entire PCIDB information for the Vendor (which includes all
// of the vendor's products, etc). Instead, we simply serialize the ID and
// human-readable name of the vendor, product, class, etc.
func (d *Device) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("{")
	b.WriteString(fmt.Sprintf("\"address\":\"%s\"", d.Address))
	b.WriteString(",\"vendor\": {")
	b.WriteString(
		fmt.Sprintf(
			"\"id\":\"%s\",\"name\":\"%s\"",
			d.Vendor.ID,
			d.Vendor.Name,
		),
	)
	b.WriteString("},")
	b.WriteString("\"product\": {")
	b.WriteString(
		fmt.Sprintf(
			"\"id\":\"%s\",\"name\":\"%s\"",
			d.Product.ID,
			d.Product.Name,
		),
	)
	b.WriteString("},")
	b.WriteString("\"subsystem\": {")
	b.WriteString(
		fmt.Sprintf(
			"\"id\":\"%s\",\"name\":\"%s\"",
			d.Subsystem.ID,
			d.Subsystem.Name,
		),
	)
	b.WriteString("},")
	b.WriteString("\"class\": {")
	b.WriteString(
		fmt.Sprintf(
			"\"id\":\"%s\",\"name\":\"%s\"",
			d.Class.ID,
			d.Class.Name,
		),
	)
	b.WriteString("},")
	b.WriteString("\"subclass\": {")
	b.WriteString(
		fmt.Sprintf(
			"\"id\":\"%s\",\"name\":\"%s\"",
			d.Subclass.ID,
			d.Subclass.Name,
		),
	)
	b.WriteString("},")
	b.WriteString("\"programming_interface\": {")
	b.WriteString(
		fmt.Sprintf(
			"\"id\":\"%s\",\"name\":\"%s\"",
			d.ProgrammingInterface.ID,
			d.ProgrammingInterface.Name,
		),
	)
	b.WriteString("}")
	b.WriteString("}")
	return b.Bytes(), nil
}

func (d *Device) String() string {
	vendorName := util.UNKNOWN
	if d.Vendor != nil {
		vendorName = d.Vendor.Name
	}
	productName := util.UNKNOWN
	if d.Product != nil {
		productName = d.Product.Name
	}
	className := util.UNKNOWN
	if d.Class != nil {
		className = d.Class.Name
	}
	return fmt.Sprintf(
		"%s -> class: '%s' vendor: '%s' product: '%s'",
		d.Address,
		className,
		vendorName,
		productName,
	)
}

type Info struct {
	ctx *context.Context
	// All PCI devices on the host system
	Devices []*Device
	// hash of class ID -> class information
	// DEPRECATED. Will be removed in v1.0. Please use
	// github.com/jaypipes/pcidb to explore PCIDB information
	Classes map[string]*pcidb.Class `json:"-"`
	// hash of vendor ID -> vendor information
	// DEPRECATED. Will be removed in v1.0. Please use
	// github.com/jaypipes/pcidb to explore PCIDB information
	Vendors map[string]*pcidb.Vendor `json:"-"`
	// hash of vendor ID + product/device ID -> product information
	// DEPRECATED. Will be removed in v1.0. Please use
	// github.com/jaypipes/pcidb to explore PCIDB information
	Products map[string]*pcidb.Product `json:"-"`
}

func (i *Info) String() string {
	return fmt.Sprintf("PCI (%d devices)", len(i.Devices))
}

type Address struct {
	Domain   string
	Bus      string
	Slot     string
	Function string
}

// Given a string address, returns a complete Address struct, filled in with
// domain, bus, slot and function components. The address string may either
// be in $BUS:$SLOT.$FUNCTION (BSF) format or it can be a full PCI address
// that includes the 4-digit $DOMAIN information as well:
// $DOMAIN:$BUS:$SLOT.$FUNCTION.
//
// Returns "" if the address string wasn't a valid PCI address.
func AddressFromString(address string) *Address {
	addrLowered := strings.ToLower(address)
	matches := regexAddress.FindStringSubmatch(addrLowered)
	if len(matches) == 6 {
		dom := "0000"
		if matches[1] != "" {
			dom = matches[2]
		}
		return &Address{
			Domain:   dom,
			Bus:      matches[3],
			Slot:     matches[4],
			Function: matches[5],
		}
	}
	return nil
}

// New returns a pointer to an Info struct that contains information about the
// PCI devices on the host system
func New(opts ...*option.Option) (*Info, error) {
	ctx := context.New(opts...)
	info := &Info{ctx: ctx}
	if err := ctx.Do(info.load); err != nil {
		return nil, err
	}
	return info, nil
}

// simple private struct used to encapsulate PCI information in a top-level
// "pci" YAML/JSON map/object key
type pciPrinter struct {
	Info *Info `json:"pci"`
}

// YAMLString returns a string with the PCI information formatted as YAML
// under a top-level "pci:" key
func (i *Info) YAMLString() string {
	return marshal.SafeYAML(pciPrinter{i})
}

// JSONString returns a string with the PCI information formatted as JSON
// under a top-level "pci:" key
func (i *Info) JSONString(indent bool) string {
	return marshal.SafeJSON(pciPrinter{i}, indent)
}
<commit_msg>pci: json: reuse json pkg Marshaller<commit_after>//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package pci

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"

	"github.com/jaypipes/pcidb"

	"github.com/jaypipes/ghw/pkg/context"
	"github.com/jaypipes/ghw/pkg/marshal"
	"github.com/jaypipes/ghw/pkg/option"
	"github.com/jaypipes/ghw/pkg/util"
)

var (
	regexAddress *regexp.Regexp = regexp.MustCompile(
		`^(([0-9a-f]{0,4}):)?([0-9a-f]{2}):([0-9a-f]{2})\.([0-9a-f]{1})$`,
	)
)

type Device struct {
	// The PCI address of the device
	Address   string         `json:"address"`
	Vendor    *pcidb.Vendor  `json:"vendor"`
	Product   *pcidb.Product `json:"product"`
	Subsystem *pcidb.Product `json:"subsystem"`
	// optional subvendor/sub-device information
	Class *pcidb.Class `json:"class"`
	// optional sub-class for the device
	Subclass *pcidb.Subclass `json:"subclass"`
	// optional programming interface
	ProgrammingInterface *pcidb.ProgrammingInterface `json:"programming_interface"`
}

type devIdent struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type devMarshallable struct {
	Address   string   `json:"address"`
	Vendor    devIdent `json:"vendor"`
	Product   devIdent `json:"product"`
	Subsystem devIdent `json:"subsystem"`
	Class     devIdent `json:"class""`
	Subclass  devIdent `json:"subclass"`
	Interface devIdent `json:"programming_interface"`
}

// NOTE(jaypipes) Device has a custom JSON marshaller because we don't want
// to serialize the entire PCIDB information for the Vendor (which includes all
// of the vendor's products, etc). Instead, we simply serialize the ID and
// human-readable name of the vendor, product, class, etc.
func (d *Device) MarshalJSON() ([]byte, error) {
	dm := devMarshallable{
		Address: d.Address,
		Vendor: devIdent{
			ID:   d.Vendor.ID,
			Name: d.Vendor.Name,
		},
		Product: devIdent{
			ID:   d.Product.ID,
			Name: d.Product.Name,
		},
		Subsystem: devIdent{
			ID:   d.Subsystem.ID,
			Name: d.Subsystem.Name,
		},
		Class: devIdent{
			ID:   d.Class.ID,
			Name: d.Class.Name,
		},
		Subclass: devIdent{
			ID:   d.Subclass.ID,
			Name: d.Subclass.Name,
		},
		Interface: devIdent{
			ID:   d.ProgrammingInterface.ID,
			Name: d.ProgrammingInterface.Name,
		},
	}
	return json.Marshal(dm)
}

func (d *Device) String() string {
	vendorName := util.UNKNOWN
	if d.Vendor != nil {
		vendorName = d.Vendor.Name
	}
	productName := util.UNKNOWN
	if d.Product != nil {
		productName = d.Product.Name
	}
	className := util.UNKNOWN
	if d.Class != nil {
		className = d.Class.Name
	}
	return fmt.Sprintf(
		"%s -> class: '%s' vendor: '%s' product: '%s'",
		d.Address,
		className,
		vendorName,
		productName,
	)
}

type Info struct {
	ctx *context.Context
	// All PCI devices on the host system
	Devices []*Device
	// hash of class ID -> class information
	// DEPRECATED. Will be removed in v1.0. Please use
	// github.com/jaypipes/pcidb to explore PCIDB information
	Classes map[string]*pcidb.Class `json:"-"`
	// hash of vendor ID -> vendor information
	// DEPRECATED. Will be removed in v1.0. Please use
	// github.com/jaypipes/pcidb to explore PCIDB information
	Vendors map[string]*pcidb.Vendor `json:"-"`
	// hash of vendor ID + product/device ID -> product information
	// DEPRECATED. Will be removed in v1.0. Please use
	// github.com/jaypipes/pcidb to explore PCIDB information
	Products map[string]*pcidb.Product `json:"-"`
}

func (i *Info) String() string {
	return fmt.Sprintf("PCI (%d devices)", len(i.Devices))
}

type Address struct {
	Domain   string
	Bus      string
	Slot     string
	Function string
}

// Given a string address, returns a complete Address struct, filled in with
// domain, bus, slot and function components. The address string may either
// be in $BUS:$SLOT.$FUNCTION (BSF) format or it can be a full PCI address
// that includes the 4-digit $DOMAIN information as well:
// $DOMAIN:$BUS:$SLOT.$FUNCTION.
//
// Returns "" if the address string wasn't a valid PCI address.
func AddressFromString(address string) *Address {
	addrLowered := strings.ToLower(address)
	matches := regexAddress.FindStringSubmatch(addrLowered)
	if len(matches) == 6 {
		dom := "0000"
		if matches[1] != "" {
			dom = matches[2]
		}
		return &Address{
			Domain:   dom,
			Bus:      matches[3],
			Slot:     matches[4],
			Function: matches[5],
		}
	}
	return nil
}

// New returns a pointer to an Info struct that contains information about the
// PCI devices on the host system
func New(opts ...*option.Option) (*Info, error) {
	ctx := context.New(opts...)
	info := &Info{ctx: ctx}
	if err := ctx.Do(info.load); err != nil {
		return nil, err
	}
	return info, nil
}

// simple private struct used to encapsulate PCI information in a top-level
// "pci" YAML/JSON map/object key
type pciPrinter struct {
	Info *Info `json:"pci"`
}

// YAMLString returns a string with the PCI information formatted as YAML
// under a top-level "pci:" key
func (i *Info) YAMLString() string {
	return marshal.SafeYAML(pciPrinter{i})
}

// JSONString returns a string with the PCI information formatted as JSON
// under a top-level "pci:" key
func (i *Info) JSONString(indent bool) string {
	return marshal.SafeJSON(pciPrinter{i}, indent)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestPasswordFailure(t *testing.T) {
	assert.False(t, validatePassword([]byte("testing"), "pow"))
}

func TestPasswordSuccess(t *testing.T) {
	assert.True(t, validatePassword([]byte("testing"), "testing"))
}

func TestUsernameFailure(t *testing.T) {
	assert.False(t, validateUsername("jdelgad", []string{"bad"}))
}

func TestUsernameSuccess(t *testing.T) {
	assert.True(t, validateUsername("jdelgad", []string{"jdelgad"}))
}

func TestPasswordFileDoesNotExist(t *testing.T) {
	users, err := openPasswordFile("fakePasswd")
	assert.Nil(t, users)
	assert.Error(t, err)
}

func TestBlankPasswordFile(t *testing.T) {
	users, err := openPasswordFile("blankPasswd")
	assert.Empty(t, users)
	assert.NoError(t, err)
}

func TestOpenPasswordFile(t *testing.T) {
	users, err := openPasswordFile("passwd")
	assert.NotEmpty(t, users)
	assert.Equal(t, len(users), 2)
	assert.NoError(t, err)

	v, ok := users["jdelgad"]
	assert.NotNil(t, ok)
	assert.Equal(t, v.username, "jdelgad")
	assert.Equal(t, v.password, "pass")
	assert.Equal(t, v.role, "Admin")
}

func TestAuthenticate(t *testing.T) {
	users, err := openPasswordFile("passwd")
	if err != nil {
		assert.True(t, false)
	}

	for name, user := range users {
		_, ok := Authenticate(name, user.password, users)
		assert.Nil(t, ok)
	}

	_, ok := Authenticate("foo", "bar", users)
	assert.NotNil(t, ok)
}

func TestRegularUser(t *testing.T) {
	users, err := openPasswordFile("passwd")

	if err != nil {
		assert.True(t, false)
	}

	v, err := isRegularUser("jdelgad", users)
	assert.False(t, v)
	assert.Nil(t, err)

	v, err = isRegularUser("newUser", users)
	assert.True(t, v)
	assert.Nil(t, err)

	v, err = isRegularUser("noSuchUser", users)
	assert.False(t, v)
	assert.NotNil(t, err)
}

func TestAdminUser(t *testing.T) {
	users, err := openPasswordFile("passwd")

	if err != nil {
		assert.True(t, false)
	}

	v, err := isAdminUser("jdelgad", users)
	assert.True(t, v)
	assert.Nil(t, err)

	v, err = isAdminUser("newUser", users)
	assert.False(t, v)
	assert.Nil(t, err)
}

func ExamplePromptUser() {
	promptUser()
	// Output:
	// Menu
	// ===========
	// 1. Logout
}

func TestIsLoggedIn(t *testing.T) {
	users, err := openPasswordFile("passwd")
	if err != nil {
		assert.True(t, false)
	}

	session, err := Authenticate("jdelgad", "pass", users)
	v := isLoggedIn("jdelgad", session)
	assert.True(t, v)
	assert.Nil(t, err)

	session, err = Authenticate("newUser", "pass2", users)
	v = isLoggedIn("newUser", session)
	assert.True(t, v)
	assert.Nil(t, err)

	v = isLoggedIn("jdelgad", session)
	assert.False(t, v)
	assert.Nil(t, err)
}
<commit_msg>fixed broken unit tests. Must stress better TDD<commit_after>package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestPasswordFailure(t *testing.T) {
	assert.False(t, validatePassword([]byte("testing"), "pow"))
}

func TestPasswordSuccess(t *testing.T) {
	assert.True(t, validatePassword([]byte("testing"), "testing"))
}

func TestUsernameFailure(t *testing.T) {
	user := User{username: "bad"}
	users := make(map[string]User, 1)
	users["bad"] = user
	assert.False(t, validateUsername("jdelgad", users))
}

func TestUsernameSuccess(t *testing.T) {
	user := User{username: "jdelgad"}
	users := make(map[string]User, 1)
	users["jdelgad"] = user
	assert.True(t, validateUsername("jdelgad", users))
}

func TestPasswordFileDoesNotExist(t *testing.T) {
	users, err := openPasswordFile("fakePasswd")
	assert.Nil(t, users)
	assert.Error(t, err)
}

func TestBlankPasswordFile(t *testing.T) {
	users, err := openPasswordFile("blankPasswd")
	assert.Empty(t, users)
	assert.NoError(t, err)
}

func TestOpenPasswordFile(t *testing.T) {
	users, err := openPasswordFile("passwd")
	assert.NotEmpty(t, users)
	assert.Equal(t, len(users), 2)
	assert.NoError(t, err)

	v, ok := users["jdelgad"]
	assert.NotNil(t, ok)
	assert.Equal(t, v.username, "jdelgad")
	assert.Equal(t, v.password, "pass")
	assert.Equal(t, v.role, "Admin")
}

func TestAuthenticate(t *testing.T) {
	users, err := openPasswordFile("passwd")
	if err != nil {
		assert.True(t, false)
	}

	for name, user := range users {
		_, ok := Authenticate(name, user.password, users)
		assert.Nil(t, ok)
	}

	_, ok := Authenticate("foo", "bar", users)
	assert.NotNil(t, ok)
}

func TestRegularUser(t *testing.T) {
	users, err := openPasswordFile("passwd")

	if err != nil {
		assert.True(t, false)
	}

	v, err := isRegularUser("jdelgad", users)
	assert.False(t, v)
	assert.Nil(t, err)

	v, err = isRegularUser("newUser", users)
	assert.True(t, v)
	assert.Nil(t, err)

	v, err = isRegularUser("noSuchUser", users)
	assert.False(t, v)
	assert.NotNil(t, err)
}

func TestAdminUser(t *testing.T) {
	users, err := openPasswordFile("passwd")

	if err != nil {
		assert.True(t, false)
	}

	v, err := isAdminUser("jdelgad", users)
	assert.True(t, v)
	assert.Nil(t, err)

	v, err = isAdminUser("newUser", users)
	assert.False(t, v)
	assert.Nil(t, err)
}

func ExamplePromptUser() {
	promptUser()
	// Output:
	// Menu
	// ===========
	// 1. Logout
}

func TestIsLoggedIn(t *testing.T) {
	users, err := openPasswordFile("passwd")
	if err != nil {
		assert.True(t, false)
	}

	session, err := Authenticate("jdelgad", "pass", users)
	v := isLoggedIn("jdelgad", session)
	assert.True(t, v)
	assert.Nil(t, err)

	session, err = Authenticate("newUser", "pass2", users)
	v = isLoggedIn("newUser", session)
	assert.True(t, v)
	assert.Nil(t, err)

	v = isLoggedIn("jdelgad", session)
	assert.False(t, v)
	assert.Nil(t, err)
}
<|endoftext|>


<commit_before>// Contains functions that don't really belong anywhere else.

package google

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"google.golang.org/api/googleapi"
)

type TerraformResourceData interface {
	HasChange(string) bool
	GetOkExists(string) (interface{}, bool)
	GetOk(string) (interface{}, bool)
	Get(string) interface{}
	Set(string, interface{}) error
	SetId(string)
	Id() string
}

type TerraformResourceDiff interface {
	GetChange(string) (interface{}, interface{})
	Get(string) interface{}
	Clear(string) error
}

// getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string {
	if zone != "" && len(zone) > 2 {
		region := zone[:len(zone)-2]
		return region
	}
	return ""
}

// Infers the region based on the following (in order of priority):
// - `region` field in resource schema
// - region extracted from the `zone` field in resource schema
// - provider-level region
// - region extracted from the provider-level zone
func getRegion(d TerraformResourceData, config *Config) (string, error) {
	return getRegionFromSchema("region", "zone", d, config)
}

// getProject reads the "project" field from the given resource data and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProject(d TerraformResourceData, config *Config) (string, error) {
	return getProjectFromSchema("project", d, config)
}

// getProjectFromDiff reads the "project" field from the given diff and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProjectFromDiff(d *schema.ResourceDiff, config *Config) (string, error) {
	res, ok := d.GetOk("project")
	if ok {
		return res.(string), nil
	}
	if config.Project != "" {
		return config.Project, nil
	}
	return "", fmt.Errorf("%s: required field is not set", "project")
}

func getRouterLockName(region string, router string) string {
	return fmt.Sprintf("router/%s/%s", region, router)
}

func handleNotFoundError(err error, d *schema.ResourceData, resource string) error {
	if isGoogleApiErrorWithCode(err, 404) {
		log.Printf("[WARN] Removing %s because it's gone", resource)
		// The resource doesn't exist anymore
		d.SetId("")

		return nil
	}

	return fmt.Errorf("Error reading %s: %s", resource, err)
}

func isGoogleApiErrorWithCode(err error, errCode int) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	return ok && gerr != nil && gerr.Code == errCode
}

func isApiNotEnabledError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 403 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "accessNotConfigured" {
			return true
		}
	}
	return false
}

func isFailedPreconditionError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 400 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "failedPrecondition" {
			return true
		}
	}
	return false
}

func isConflictError(err error) bool {
	if e, ok := err.(*googleapi.Error); ok && e.Code == 409 {
		return true
	} else if !ok && errwrap.ContainsType(err, &googleapi.Error{}) {
		e := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
		if e.Code == 409 {
			return true
		}
	}
	return false
}

// expandLabels pulls the value of "labels" out of a TerraformResourceData as a map[string]string.
func expandLabels(d TerraformResourceData) map[string]string {
	return expandStringMap(d, "labels")
}

// expandEnvironmentVariables pulls the value of "environment_variables" out of a schema.ResourceData as a map[string]string.
func expandEnvironmentVariables(d *schema.ResourceData) map[string]string {
	return expandStringMap(d, "environment_variables")
}

// expandStringMap pulls the value of key out of a TerraformResourceData as a map[string]string.
func expandStringMap(d TerraformResourceData, key string) map[string]string {
	v, ok := d.GetOk(key)

	if !ok {
		return map[string]string{}
	}

	return convertStringMap(v.(map[string]interface{}))
}

func convertStringMap(v map[string]interface{}) map[string]string {
	m := make(map[string]string)
	for k, val := range v {
		m[k] = val.(string)
	}
	return m
}

func convertStringArr(ifaceArr []interface{}) []string {
	return convertAndMapStringArr(ifaceArr, func(s string) string { return s })
}

func convertAndMapStringArr(ifaceArr []interface{}, f func(string) string) []string {
	var arr []string
	for _, v := range ifaceArr {
		if v == nil {
			continue
		}
		arr = append(arr, f(v.(string)))
	}
	return arr
}

func mapStringArr(original []string, f func(string) string) []string {
	var arr []string
	for _, v := range original {
		arr = append(arr, f(v))
	}
	return arr
}

func convertStringArrToInterface(strs []string) []interface{} {
	arr := make([]interface{}, len(strs))
	for i, str := range strs {
		arr[i] = str
	}
	return arr
}

func convertStringSet(set *schema.Set) []string {
	s := make([]string, 0, set.Len())
	for _, v := range set.List() {
		s = append(s, v.(string))
	}
	sort.Strings(s)

	return s
}

func golangSetFromStringSlice(strings []string) map[string]struct{} {
	set := map[string]struct{}{}
	for _, v := range strings {
		set[v] = struct{}{}
	}

	return set
}

func stringSliceFromGolangSet(sset map[string]struct{}) []string {
	ls := make([]string, 0, len(sset))
	for s := range sset {
		ls = append(ls, s)
	}
	sort.Strings(ls)

	return ls
}

func reverseStringMap(m map[string]string) map[string]string {
	o := map[string]string{}
	for k, v := range m {
		o[v] = k
	}
	return o
}

func mergeStringMaps(a, b map[string]string) map[string]string {
	merged := make(map[string]string)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeSchemas(a, b map[string]*schema.Schema) map[string]*schema.Schema {
	merged := make(map[string]*schema.Schema)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeResourceMaps(ms ...map[string]*schema.Resource) (map[string]*schema.Resource, error) {
	merged := make(map[string]*schema.Resource)
	duplicates := []string{}

	for _, m := range ms {
		for k, v := range m {
			if _, ok := merged[k]; ok {
				duplicates = append(duplicates, k)
			}

			merged[k] = v
		}
	}

	var err error
	if len(duplicates) > 0 {
		err = fmt.Errorf("saw duplicates in mergeResourceMaps: %v", duplicates)
	}

	return merged, err
}

func extractFirstMapConfig(m []interface{}) map[string]interface{} {
	if len(m) == 0 {
		return map[string]interface{}{}
	}

	return m[0].(map[string]interface{})
}

func lockedCall(lockKey string, f func() error) error {
	mutexKV.Lock(lockKey)
	defer mutexKV.Unlock(lockKey)

	return f()
}

// This is a Printf sibling (Nprintf; Named Printf), which handles strings like
// Nprintf("Hello %{target}!", map[string]interface{}{"target":"world"}) == "Hello world!".
// This is particularly useful for generated tests, where we don't want to use Printf,
// since that would require us to generate a very particular ordering of arguments.
func Nprintf(format string, params map[string]interface{}) string {
	for key, val := range params {
		format = strings.Replace(format, "%{"+key+"}", fmt.Sprintf("%v", val), -1)
	}
	return format
}

// serviceAccountFQN will attempt to generate the fully qualified name in the format of:
// "projects/(-|<project>)/serviceAccounts/<service_account_id>@<project>.iam.gserviceaccount.com"
// A project is required if we are trying to build the FQN from a service account id and
// and error will be returned in this case if no project is set in the resource or the
// provider-level config
func serviceAccountFQN(serviceAccount string, d TerraformResourceData, config *Config) (string, error) {
	// If the service account id is already the fully qualified name
	if strings.HasPrefix(serviceAccount, "projects/") {
		return serviceAccount, nil
	}

	// If the service account id is an email
	if strings.Contains(serviceAccount, "@") {
		return "projects/-/serviceAccounts/" + serviceAccount, nil
	}

	// Get the project from the resource or fallback to the project
	// in the provider configuration
	project, err := getProject(d, config)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("projects/-/serviceAccounts/%s@%s.iam.gserviceaccount.com", serviceAccount, project), nil
}

func paginatedListRequest(project, baseUrl string, config *Config, flattener func(map[string]interface{}) []interface{}) ([]interface{}, error) {
	res, err := sendRequest(config, "GET", project, baseUrl, nil)
	if err != nil {
		return nil, err
	}

	ls := flattener(res)
	pageToken, ok := res["pageToken"]
	for ok {
		if pageToken.(string) == "" {
			break
		}
		url := fmt.Sprintf("%s?pageToken=%s", baseUrl, pageToken.(string))
		res, err = sendRequest(config, "GET", project, url, nil)
		if err != nil {
			return nil, err
		}
		ls = append(ls, flattener(res))
		pageToken, ok = res["pageToken"]
	}

	return ls, nil
}

func getInterconnectAttachmentLink(config *Config, project, region, ic string) (string, error) {
	if !strings.Contains(ic, "/") {
		icData, err := config.clientCompute.InterconnectAttachments.Get(
			project, region, ic).Do()
		if err != nil {
			return "", fmt.Errorf("Error reading interconnect attachment: %s", err)
		}
		ic = icData.SelfLink
	}

	return ic, nil
}

// Given two sets of references (with "from" values in self link form),
// determine which need to be added or removed // during an update using
// addX/removeX APIs.
func calcAddRemove(from []string, to []string) (add, remove []string) {
	add = make([]string, 0)
	remove = make([]string, 0)
	for _, u := range to {
		found := false
		for _, v := range from {
			if compareSelfLinkOrResourceName("", v, u, nil) {
				found = true
				break
			}
		}
		if !found {
			add = append(add, u)
		}
	}
	for _, u := range from {
		found := false
		for _, v := range to {
			if compareSelfLinkOrResourceName("", u, v, nil) {
				found = true
				break
			}
		}
		if !found {
			remove = append(remove, u)
		}
	}
	return add, remove
}

func stringInSlice(arr []string, str string) bool {
	for _, i := range arr {
		if i == str {
			return true
		}
	}

	return false
}

func migrateStateNoop(v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
	return is, nil
}

func expandString(v interface{}, d TerraformResourceData, config *Config) (string, error) {
	return v.(string), nil
}
<commit_msg>Allow update of node pool workload metadata config (#3512) (#443)<commit_after>// Contains functions that don't really belong anywhere else.

package google

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"google.golang.org/api/googleapi"
)

type TerraformResourceData interface {
	HasChange(string) bool
	GetOkExists(string) (interface{}, bool)
	GetOk(string) (interface{}, bool)
	Get(string) interface{}
	Set(string, interface{}) error
	SetId(string)
	Id() string
}

type TerraformResourceDiff interface {
	GetChange(string) (interface{}, interface{})
	Get(string) interface{}
	Clear(string) error
}

// getRegionFromZone returns the region from a zone for Google cloud.
func getRegionFromZone(zone string) string {
	if zone != "" && len(zone) > 2 {
		region := zone[:len(zone)-2]
		return region
	}
	return ""
}

// Infers the region based on the following (in order of priority):
// - `region` field in resource schema
// - region extracted from the `zone` field in resource schema
// - provider-level region
// - region extracted from the provider-level zone
func getRegion(d TerraformResourceData, config *Config) (string, error) {
	return getRegionFromSchema("region", "zone", d, config)
}

// getProject reads the "project" field from the given resource data and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProject(d TerraformResourceData, config *Config) (string, error) {
	return getProjectFromSchema("project", d, config)
}

// getProjectFromDiff reads the "project" field from the given diff and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProjectFromDiff(d *schema.ResourceDiff, config *Config) (string, error) {
	res, ok := d.GetOk("project")
	if ok {
		return res.(string), nil
	}
	if config.Project != "" {
		return config.Project, nil
	}
	return "", fmt.Errorf("%s: required field is not set", "project")
}

func getRouterLockName(region string, router string) string {
	return fmt.Sprintf("router/%s/%s", region, router)
}

func handleNotFoundError(err error, d *schema.ResourceData, resource string) error {
	if isGoogleApiErrorWithCode(err, 404) {
		log.Printf("[WARN] Removing %s because it's gone", resource)
		// The resource doesn't exist anymore
		d.SetId("")

		return nil
	}

	return fmt.Errorf("Error reading %s: %s", resource, err)
}

func isGoogleApiErrorWithCode(err error, errCode int) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	return ok && gerr != nil && gerr.Code == errCode
}

func isApiNotEnabledError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 403 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "accessNotConfigured" {
			return true
		}
	}
	return false
}

func isFailedPreconditionError(err error) bool {
	gerr, ok := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
	if !ok {
		return false
	}
	if gerr == nil {
		return false
	}
	if gerr.Code != 400 {
		return false
	}
	for _, e := range gerr.Errors {
		if e.Reason == "failedPrecondition" {
			return true
		}
	}
	return false
}

func isConflictError(err error) bool {
	if e, ok := err.(*googleapi.Error); ok && e.Code == 409 {
		return true
	} else if !ok && errwrap.ContainsType(err, &googleapi.Error{}) {
		e := errwrap.GetType(err, &googleapi.Error{}).(*googleapi.Error)
		if e.Code == 409 {
			return true
		}
	}
	return false
}

// expandLabels pulls the value of "labels" out of a TerraformResourceData as a map[string]string.
func expandLabels(d TerraformResourceData) map[string]string {
	return expandStringMap(d, "labels")
}

// expandEnvironmentVariables pulls the value of "environment_variables" out of a schema.ResourceData as a map[string]string.
func expandEnvironmentVariables(d *schema.ResourceData) map[string]string {
	return expandStringMap(d, "environment_variables")
}

// expandStringMap pulls the value of key out of a TerraformResourceData as a map[string]string.
func expandStringMap(d TerraformResourceData, key string) map[string]string {
	v, ok := d.GetOk(key)

	if !ok {
		return map[string]string{}
	}

	return convertStringMap(v.(map[string]interface{}))
}

func convertStringMap(v map[string]interface{}) map[string]string {
	m := make(map[string]string)
	for k, val := range v {
		m[k] = val.(string)
	}
	return m
}

func convertStringArr(ifaceArr []interface{}) []string {
	return convertAndMapStringArr(ifaceArr, func(s string) string { return s })
}

func convertAndMapStringArr(ifaceArr []interface{}, f func(string) string) []string {
	var arr []string
	for _, v := range ifaceArr {
		if v == nil {
			continue
		}
		arr = append(arr, f(v.(string)))
	}
	return arr
}

func mapStringArr(original []string, f func(string) string) []string {
	var arr []string
	for _, v := range original {
		arr = append(arr, f(v))
	}
	return arr
}

func convertStringArrToInterface(strs []string) []interface{} {
	arr := make([]interface{}, len(strs))
	for i, str := range strs {
		arr[i] = str
	}
	return arr
}

func convertStringSet(set *schema.Set) []string {
	s := make([]string, 0, set.Len())
	for _, v := range set.List() {
		s = append(s, v.(string))
	}
	sort.Strings(s)

	return s
}

func golangSetFromStringSlice(strings []string) map[string]struct{} {
	set := map[string]struct{}{}
	for _, v := range strings {
		set[v] = struct{}{}
	}

	return set
}

func stringSliceFromGolangSet(sset map[string]struct{}) []string {
	ls := make([]string, 0, len(sset))
	for s := range sset {
		ls = append(ls, s)
	}
	sort.Strings(ls)

	return ls
}

func reverseStringMap(m map[string]string) map[string]string {
	o := map[string]string{}
	for k, v := range m {
		o[v] = k
	}
	return o
}

func mergeStringMaps(a, b map[string]string) map[string]string {
	merged := make(map[string]string)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeSchemas(a, b map[string]*schema.Schema) map[string]*schema.Schema {
	merged := make(map[string]*schema.Schema)

	for k, v := range a {
		merged[k] = v
	}

	for k, v := range b {
		merged[k] = v
	}

	return merged
}

func mergeResourceMaps(ms ...map[string]*schema.Resource) (map[string]*schema.Resource, error) {
	merged := make(map[string]*schema.Resource)
	duplicates := []string{}

	for _, m := range ms {
		for k, v := range m {
			if _, ok := merged[k]; ok {
				duplicates = append(duplicates, k)
			}

			merged[k] = v
		}
	}

	var err error
	if len(duplicates) > 0 {
		err = fmt.Errorf("saw duplicates in mergeResourceMaps: %v", duplicates)
	}

	return merged, err
}

func extractFirstMapConfig(m []interface{}) map[string]interface{} {
	if len(m) == 0 {
		return map[string]interface{}{}
	}

	return m[0].(map[string]interface{})
}

func lockedCall(lockKey string, f func() error) error {
	mutexKV.Lock(lockKey)
	defer mutexKV.Unlock(lockKey)

	return f()
}

// This is a Printf sibling (Nprintf; Named Printf), which handles strings like
// Nprintf("Hello %{target}!", map[string]interface{}{"target":"world"}) == "Hello world!".
// This is particularly useful for generated tests, where we don't want to use Printf,
// since that would require us to generate a very particular ordering of arguments.
func Nprintf(format string, params map[string]interface{}) string {
	for key, val := range params {
		format = strings.Replace(format, "%{"+key+"}", fmt.Sprintf("%v", val), -1)
	}
	return format
}

// serviceAccountFQN will attempt to generate the fully qualified name in the format of:
// "projects/(-|<project>)/serviceAccounts/<service_account_id>@<project>.iam.gserviceaccount.com"
// A project is required if we are trying to build the FQN from a service account id and
// and error will be returned in this case if no project is set in the resource or the
// provider-level config
func serviceAccountFQN(serviceAccount string, d TerraformResourceData, config *Config) (string, error) {
	// If the service account id is already the fully qualified name
	if strings.HasPrefix(serviceAccount, "projects/") {
		return serviceAccount, nil
	}

	// If the service account id is an email
	if strings.Contains(serviceAccount, "@") {
		return "projects/-/serviceAccounts/" + serviceAccount, nil
	}

	// Get the project from the resource or fallback to the project
	// in the provider configuration
	project, err := getProject(d, config)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("projects/-/serviceAccounts/%s@%s.iam.gserviceaccount.com", serviceAccount, project), nil
}

func paginatedListRequest(project, baseUrl string, config *Config, flattener func(map[string]interface{}) []interface{}) ([]interface{}, error) {
	res, err := sendRequest(config, "GET", project, baseUrl, nil)
	if err != nil {
		return nil, err
	}

	ls := flattener(res)
	pageToken, ok := res["pageToken"]
	for ok {
		if pageToken.(string) == "" {
			break
		}
		url := fmt.Sprintf("%s?pageToken=%s", baseUrl, pageToken.(string))
		res, err = sendRequest(config, "GET", project, url, nil)
		if err != nil {
			return nil, err
		}
		ls = append(ls, flattener(res))
		pageToken, ok = res["pageToken"]
	}

	return ls, nil
}

func getInterconnectAttachmentLink(config *Config, project, region, ic string) (string, error) {
	if !strings.Contains(ic, "/") {
		icData, err := config.clientCompute.InterconnectAttachments.Get(
			project, region, ic).Do()
		if err != nil {
			return "", fmt.Errorf("Error reading interconnect attachment: %s", err)
		}
		ic = icData.SelfLink
	}

	return ic, nil
}

// Given two sets of references (with "from" values in self link form),
// determine which need to be added or removed // during an update using
// addX/removeX APIs.
func calcAddRemove(from []string, to []string) (add, remove []string) {
	add = make([]string, 0)
	remove = make([]string, 0)
	for _, u := range to {
		found := false
		for _, v := range from {
			if compareSelfLinkOrResourceName("", v, u, nil) {
				found = true
				break
			}
		}
		if !found {
			add = append(add, u)
		}
	}
	for _, u := range from {
		found := false
		for _, v := range to {
			if compareSelfLinkOrResourceName("", u, v, nil) {
				found = true
				break
			}
		}
		if !found {
			remove = append(remove, u)
		}
	}
	return add, remove
}

func stringInSlice(arr []string, str string) bool {
	for _, i := range arr {
		if i == str {
			return true
		}
	}

	return false
}

func migrateStateNoop(v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
	return is, nil
}

func expandString(v interface{}, d TerraformResourceData, config *Config) (string, error) {
	return v.(string), nil
}

func changeFieldSchemaToForceNew(sch *schema.Schema) {
	sch.ForceNew = true
	switch sch.Type {
	case schema.TypeList:
	case schema.TypeSet:
		if nestedR, ok := sch.Elem.(*schema.Resource); ok {
			for _, nestedSch := range nestedR.Schema {
				changeFieldSchemaToForceNew(nestedSch)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	"crypto/aes"
	"crypto/sha256"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"syscall"

	"github.com/starius/pasta/gopasta/database"
	"github.com/starius/pasta/gopasta/server"
	"github.com/tyler-smith/go-bip39/wordlists"
	"gitlab.com/NebulousLabs/entropy-mnemonics"
	"gitlab.com/NebulousLabs/fastrand"
	"gitlab.com/starius/fpe/phrase"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/ssh/terminal"
)

var (
	dir          = flag.String("dir", ".", "Directory to store data in")
	listen       = flag.String("listen", ":8042", "Address to listen on")
	letsDomains  = flag.String("letsencrypt-domains", "", "Run Let's Encrypt on these domains")
	secretFile   = flag.String("secret-file", "", "Secret file")
	genSecret    = flag.Bool("gen-secret", false, "Generate random and exit")
	printAdmin   = flag.Bool("print-admin-auth", false, "Print admin Authorization header and exit")
	maxSize      = flag.Int("max-size", 10*1024*1024, "Max record size, bytes")
	cacheRecords = flag.Int("cache-records", 10000, "Cache size, records")
	cacheBytes   = flag.Int("cache-bytes", 100*1024*1024, "Cache size, bytes")
)

const saltHex = "b59e698ae2b5893a2a45edf3f809ef5977aa9b3526fbb76cf188817d6bbf19e3"
const databaseInfo = "database"
const idInfo = "id"
const adminAuthInfo = "adminAuth"

func redirectToHTTPS(w http.ResponseWriter, r *http.Request) {
	url := "https://" + r.Host + r.URL.Path
	if len(r.URL.RawQuery) > 0 {
		url += "?" + r.URL.RawQuery
	}
	http.Redirect(w, r, url, http.StatusPermanentRedirect)
}

func main() {
	flag.Parse()
	if *genSecret {
		secret := fastrand.Bytes(16)
		p, err := mnemonics.ToPhrase(secret, mnemonics.English)
		if err != nil {
			panic(err)
		}
		fmt.Println(p.String())
		os.Exit(0)
	}
	if *dir == "" {
		log.Fatalf("Please specify -dir")
	}
	var err error
	var secret []byte
	if *secretFile != "" {
		secret, err = ioutil.ReadFile(*secretFile)
		if err != nil {
			log.Fatalf("Failed to read secret file")
		}
	} else {
		fmt.Print("Enter secret: ")
		secret, err = terminal.ReadPassword(int(syscall.Stdin))
		fmt.Println()
		if err != nil {
			log.Fatalf("Failed to read secret from keyboard")
		}
	}
	secret = bytes.TrimSpace(secret)
	mainKey, err := mnemonics.FromString(string(secret), mnemonics.English)
	if err != nil {
		log.Fatalf("Failed to decode the secret")
	}
	salt, err := hex.DecodeString(saltHex)
	if err != nil {
		panic(err)
	}

	adminAuthHKDF := hkdf.New(sha256.New, mainKey, salt, []byte(adminAuthInfo))
	adminAuthBytes := make([]byte, 16)
	if _, err := io.ReadFull(adminAuthHKDF, adminAuthBytes); err != nil {
		panic(err)
	}
	adminAuth := hex.EncodeToString(adminAuthBytes)
	if *printAdmin {
		fmt.Println(adminAuth)
		os.Exit(0)
	}

	dbHKDF := hkdf.New(sha256.New, mainKey, salt, []byte(databaseInfo))
	indexKey := make([]byte, 16)
	if _, err := io.ReadFull(dbHKDF, indexKey); err != nil {
		panic(err)
	}
	dataKey := make([]byte, 16)
	if _, err := io.ReadFull(dbHKDF, dataKey); err != nil {
		panic(err)
	}
	index, err := os.OpenFile(*dir+"/index", os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		panic(err)
	}
	data, err := os.OpenFile(*dir+"/data", os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		panic(err)
	}
	indexBlock, err := aes.NewCipher(indexKey)
	if err != nil {
		panic(err)
	}
	dataBlock, err := aes.NewCipher(dataKey)
	if err != nil {
		panic(err)
	}
	db, err := database.NewDatabase(index, data, indexBlock, dataBlock, *maxSize, *cacheRecords, *cacheBytes)
	if err != nil {
		panic(err)
	}

	idHKDF := hkdf.New(sha256.New, mainKey, salt, []byte(idInfo))
	wordsKey := make([]byte, 16)
	if _, err := io.ReadFull(idHKDF, wordsKey); err != nil {
		panic(err)
	}
	longKey := make([]byte, 16)
	if _, err := io.ReadFull(idHKDF, longKey); err != nil {
		panic(err)
	}
	wordsBlock, err := aes.NewCipher(wordsKey)
	if err != nil {
		panic(err)
	}
	longBlock, err := aes.NewCipher(longKey)
	if err != nil {
		panic(err)
	}
	idEncoder, err := phrase.NewIDEncoder(wordsBlock, longBlock, wordlists.English)
	if err != nil {
		panic(err)
	}

	handler := server.NewHandler(db, idEncoder, *maxSize, adminAuth)

	if *letsDomains != "" {
		// Use Let's Encrypt.
		set := make(map[string]struct{})
		for _, host := range strings.Split(*letsDomains, ",") {
			host = strings.TrimSpace(host)
			set[host] = struct{}{}
		}
		errHost := fmt.Errorf("bad host")
		hostPolicy := func(ctx context.Context, host string) error {
			if _, has := set[host]; has {
				return nil
			} else {
				return errHost
			}
		}
		m := &autocert.Manager{
			Cache:      autocert.DirCache(*dir),
			Prompt:     autocert.AcceptTOS,
			HostPolicy: hostPolicy,
		}
		certHandler := m.HTTPHandler(http.HandlerFunc(redirectToHTTPS))
		listener := m.Listener()
		go func() {
			log.Fatal(http.ListenAndServe(":80", certHandler))
		}()
		go func() {
			log.Fatal(http.Serve(listener, handler))
		}()
	}

	log.Fatal(http.ListenAndServe(*listen, handler))
}
<commit_msg>gopasta: use autocert.HostWhitelist<commit_after>package main

import (
	"bytes"
	"crypto/aes"
	"crypto/sha256"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"syscall"

	"github.com/starius/pasta/gopasta/database"
	"github.com/starius/pasta/gopasta/server"
	"github.com/tyler-smith/go-bip39/wordlists"
	"gitlab.com/NebulousLabs/entropy-mnemonics"
	"gitlab.com/NebulousLabs/fastrand"
	"gitlab.com/starius/fpe/phrase"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/ssh/terminal"
)

var (
	dir          = flag.String("dir", ".", "Directory to store data in")
	listen       = flag.String("listen", ":8042", "Address to listen on")
	letsDomains  = flag.String("letsencrypt-domains", "", "Run Let's Encrypt on these domains")
	secretFile   = flag.String("secret-file", "", "Secret file")
	genSecret    = flag.Bool("gen-secret", false, "Generate random and exit")
	printAdmin   = flag.Bool("print-admin-auth", false, "Print admin Authorization header and exit")
	maxSize      = flag.Int("max-size", 10*1024*1024, "Max record size, bytes")
	cacheRecords = flag.Int("cache-records", 10000, "Cache size, records")
	cacheBytes   = flag.Int("cache-bytes", 100*1024*1024, "Cache size, bytes")
)

const saltHex = "b59e698ae2b5893a2a45edf3f809ef5977aa9b3526fbb76cf188817d6bbf19e3"
const databaseInfo = "database"
const idInfo = "id"
const adminAuthInfo = "adminAuth"

func redirectToHTTPS(w http.ResponseWriter, r *http.Request) {
	url := "https://" + r.Host + r.URL.Path
	if len(r.URL.RawQuery) > 0 {
		url += "?" + r.URL.RawQuery
	}
	http.Redirect(w, r, url, http.StatusPermanentRedirect)
}

func main() {
	flag.Parse()
	if *genSecret {
		secret := fastrand.Bytes(16)
		p, err := mnemonics.ToPhrase(secret, mnemonics.English)
		if err != nil {
			panic(err)
		}
		fmt.Println(p.String())
		os.Exit(0)
	}
	if *dir == "" {
		log.Fatalf("Please specify -dir")
	}
	var err error
	var secret []byte
	if *secretFile != "" {
		secret, err = ioutil.ReadFile(*secretFile)
		if err != nil {
			log.Fatalf("Failed to read secret file")
		}
	} else {
		fmt.Print("Enter secret: ")
		secret, err = terminal.ReadPassword(int(syscall.Stdin))
		fmt.Println()
		if err != nil {
			log.Fatalf("Failed to read secret from keyboard")
		}
	}
	secret = bytes.TrimSpace(secret)
	mainKey, err := mnemonics.FromString(string(secret), mnemonics.English)
	if err != nil {
		log.Fatalf("Failed to decode the secret")
	}
	salt, err := hex.DecodeString(saltHex)
	if err != nil {
		panic(err)
	}

	adminAuthHKDF := hkdf.New(sha256.New, mainKey, salt, []byte(adminAuthInfo))
	adminAuthBytes := make([]byte, 16)
	if _, err := io.ReadFull(adminAuthHKDF, adminAuthBytes); err != nil {
		panic(err)
	}
	adminAuth := hex.EncodeToString(adminAuthBytes)
	if *printAdmin {
		fmt.Println(adminAuth)
		os.Exit(0)
	}

	dbHKDF := hkdf.New(sha256.New, mainKey, salt, []byte(databaseInfo))
	indexKey := make([]byte, 16)
	if _, err := io.ReadFull(dbHKDF, indexKey); err != nil {
		panic(err)
	}
	dataKey := make([]byte, 16)
	if _, err := io.ReadFull(dbHKDF, dataKey); err != nil {
		panic(err)
	}
	index, err := os.OpenFile(*dir+"/index", os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		panic(err)
	}
	data, err := os.OpenFile(*dir+"/data", os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		panic(err)
	}
	indexBlock, err := aes.NewCipher(indexKey)
	if err != nil {
		panic(err)
	}
	dataBlock, err := aes.NewCipher(dataKey)
	if err != nil {
		panic(err)
	}
	db, err := database.NewDatabase(index, data, indexBlock, dataBlock, *maxSize, *cacheRecords, *cacheBytes)
	if err != nil {
		panic(err)
	}

	idHKDF := hkdf.New(sha256.New, mainKey, salt, []byte(idInfo))
	wordsKey := make([]byte, 16)
	if _, err := io.ReadFull(idHKDF, wordsKey); err != nil {
		panic(err)
	}
	longKey := make([]byte, 16)
	if _, err := io.ReadFull(idHKDF, longKey); err != nil {
		panic(err)
	}
	wordsBlock, err := aes.NewCipher(wordsKey)
	if err != nil {
		panic(err)
	}
	longBlock, err := aes.NewCipher(longKey)
	if err != nil {
		panic(err)
	}
	idEncoder, err := phrase.NewIDEncoder(wordsBlock, longBlock, wordlists.English)
	if err != nil {
		panic(err)
	}

	handler := server.NewHandler(db, idEncoder, *maxSize, adminAuth)

	if *letsDomains != "" {
		// Use Let's Encrypt.
		domains := strings.Split(*letsDomains, ",")
		m := &autocert.Manager{
			Cache:      autocert.DirCache(*dir),
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(domains...),
		}
		certHandler := m.HTTPHandler(http.HandlerFunc(redirectToHTTPS))
		listener := m.Listener()
		go func() {
			log.Fatal(http.ListenAndServe(":80", certHandler))
		}()
		go func() {
			log.Fatal(http.Serve(listener, handler))
		}()
	}

	log.Fatal(http.ListenAndServe(*listen, handler))
}
<|endoftext|>