<commit_before>package main

import (
    "./commands"

    "net/http"
    "html/template"
    "fmt"
    "github.com/twinj/uuid"
    "time"

    "os"
    "io/ioutil"
    "encoding/json"
    "github.com/DisposaBoy/JsonConfigReader"

    "strings"
    "strconv"
)

type TemplateModel struct {
    Motd string
    Commands []commands.DisplayCommand
}

type Token struct {
    cookie *http.Cookie
    username string
}

var (
    templateCollection *template.Template
    validTokens []Token
    config commands.JsonObject
    users map[string]string
    templateModels map[string]TemplateModel
)

const pioneerAccessToken = "pioneer-access-token"

func main() {
    loadConfig()
    commands.ParseCommands(config)

    templateModels = make(map[string]TemplateModel)
    for user := range users {
        templateModels[user] = TemplateModel{Motd: config["motd"].(string), Commands: getCommandsForUser(user)}
    }

    templateCollection = template.Must(template.ParseFiles("html/login.html", "html/main.html"))

    http.HandleFunc("/login", loginHandler)
    http.HandleFunc("/main", mainHandler)
    http.HandleFunc("/api/", apiHandler)
    http.Handle("/js/", http.FileServer(http.Dir("./assets")))
    http.Handle("/css/", http.FileServer(http.Dir("./assets")))
    http.Handle("/img/", http.FileServer(http.Dir("./assets")))
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
        http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
    })

    var err interface{}
    if strings.ToLower(config["ssl"].(string)) == "true" {
        fmt.Println("Starting https server...")
        err = http.ListenAndServeTLS(":443", config["certFile"].(string), config["keyFile"].(string), nil)
    } else {
        fmt.Println("Starting http server...")
        fmt.Println("[WARNING] SSL encryption disabled! This is not recommended, as securely logging in is not possible without it!")
        err = http.ListenAndServe(":80", nil)
    }

    if err != nil {
        panic(fmt.Sprintf("ListenAndServe: %s", err))
    }
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
    cookie, cerr := r.Cookie(pioneerAccessToken)
    valid, _ := cookieIsValid(cookie)
    if cerr == nil && valid {
        http.Redirect(w, r, "/main", http.StatusTemporaryRedirect)
        return
    }
    err := templateCollection.ExecuteTemplate(w, "login.html", nil)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
    cookie, err := r.Cookie(pioneerAccessToken)
    valid, token := cookieIsValid(cookie)
    if err != nil || !valid {
        http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
        return
    }

    terr := templateCollection.ExecuteTemplate(w, "main.html", templateModels[token.username])
    if terr != nil {
        http.Error(w, terr.Error(), http.StatusInternalServerError)
    }
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "You can only POST to this api!", http.StatusBadRequest)
        return
    }
    command := r.URL.Path[len("/api/"):]
    slashIndex := strings.Index(command, "/")
    if slashIndex != -1 {
        command = command[:slashIndex]
    }
    switch command {
    case "login":
        body, error := ioutil.ReadAll(r.Body)
        valid, username := isValidLogin(string(body))
        if error == nil && valid {
            u := uuid.NewV4()
            cookie := &http.Cookie{Name: pioneerAccessToken, Value: u.String(), Expires: time.Now().Add(30*time.Minute), Path: "/"}
            validTokens = append(validTokens, Token{cookie: cookie, username: username})
            http.SetCookie(w, cookie)
            fmt.Fprintln(w, u.String())
        } else {
            http.Error(w, "Wrong login!", 403)
        }

        break
    case "logout":
        cookie, err := r.Cookie(pioneerAccessToken)
        if err != nil {
            http.Error(w, "Unauthorized", 403)
            return
        }

        for i := 0; i < len(validTokens); i++ {
            if cookie.Value == validTokens[i].cookie.Value {
                validTokens[i].cookie.Expires = time.Now().Add(-1 * time.Minute)
            }
        }

        break
    case "getcmds":
        cookie, err := r.Cookie(pioneerAccessToken)
        valid, token := cookieIsValid(cookie)
        if err != nil || !valid {
            http.Error(w, "Unauthorized", 403)
            return
        }

        retval := "{"
        for i, cmd := range templateModels[token.username].Commands {
            if i > 0 {
                retval += ","
            }
            retval += "\"" + cmd.Name + "\": " + strconv.Itoa(cmd.ID)
        }
        retval += "}"

        fmt.Fprint(w, retval)

        break;
    case "cmd":
        cookie, err := r.Cookie(pioneerAccessToken)
        valid, token := cookieIsValid(cookie)
        if err != nil || !valid {
            http.Error(w, "Unauthorized", 403)
            return
        }

        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "Parameter error", 500)
            return
        }

        origCmd := r.URL.Path[len("/api/"):]
        if len(origCmd) < 5 {
            http.Error(w, "Parameter error", 500)
            return
        }

        id, serr := strconv.Atoi(origCmd[slashIndex + 1:])
        if serr != nil {
            http.Error(w, "Parameter error", 500)
            return
        }

        cmd, ok := commands.CommandsAvailable[id]
        if ok && in(cmd.AllowedUsers, token.username) {
            retval := cmd.ExecutableCommand.Execute(string(body))
            fmt.Fprint(w, retval)
            return
        }

        http.Error(w, "Command not found", 404)

        break
    }
}

func in(haystack []string, needle string) bool {
    for _, h := range haystack {
        if h == needle {
            return true
        }
    }

    return false
}

func getCommandsForUser(username string) []commands.DisplayCommand {
    var retval []commands.DisplayCommand
    for _, cmd := range commands.CommandsAvailable {
        if in(cmd.AllowedUsers, username) {
            retval = append(retval, cmd)
        }
    }
    return retval
}

func isValidLogin(body string) (bool, string) {
    nPos := strings.Index(body, "\n")
    if nPos < 0 || len(body) <= nPos + 1 {
        return false, ""
    }

    username := body[:nPos]
    password := body[nPos + 1:]

    expectedPass, ok := users[username]
    if !ok || expectedPass != password {
        return false, ""
    }

    return true, username
}

func cookieIsValid(cookie *http.Cookie) (bool,*Token) {
    for i := 0; i < len(validTokens); i++ {
        if time.Now().After(validTokens[i].cookie.Expires) {
            validTokens = append(validTokens[:i], validTokens[i+1:]...)
            i--
            continue
        }
        if cookie.Value == validTokens[i].cookie.Value {
            return true,&validTokens[i]
        }
    }

    return false,nil
}

func loadConfig() {
    var v interface{}
    f, _ := os.Open("config.json")
    r := JsonConfigReader.New(f)
    json.NewDecoder(r).Decode(&v)
    config = v.(map[string]interface{})

    users = make(map[string]string)
    u := config["users"].([]interface{})
    for _, us := range u {
        use := us.(map[string]interface{})
        users[use["username"].(string)] = use["password"].(string)
    }
}<commit_msg>Another hotfix<commit_after>package main

import (
    "./commands"

    "net/http"
    "html/template"
    "fmt"
    "github.com/twinj/uuid"
    "time"

    "os"
    "io/ioutil"
    "encoding/json"
    "github.com/DisposaBoy/JsonConfigReader"

    "strings"
    "strconv"
)

type TemplateModel struct {
    Motd string
    Commands []commands.DisplayCommand
}

type Token struct {
    cookie *http.Cookie
    username string
}

var (
    templateCollection *template.Template
    validTokens []Token
    config commands.JsonObject
    users map[string]string
    templateModels map[string]TemplateModel
)

const pioneerAccessToken = "pioneer-access-token"

func main() {
    loadConfig()
    commands.ParseCommands(config)

    templateModels = make(map[string]TemplateModel)
    for user := range users {
        templateModels[user] = TemplateModel{Motd: config["motd"].(string), Commands: getCommandsForUser(user)}
    }

    templateCollection = template.Must(template.ParseFiles("html/login.html", "html/main.html"))

    http.HandleFunc("/login", loginHandler)
    http.HandleFunc("/main", mainHandler)
    http.HandleFunc("/api/", apiHandler)
    http.Handle("/js/", http.FileServer(http.Dir("./assets")))
    http.Handle("/css/", http.FileServer(http.Dir("./assets")))
    http.Handle("/img/", http.FileServer(http.Dir("./assets")))
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
        http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
    })

    var err interface{}
    if strings.ToLower(config["ssl"].(string)) == "true" {
        fmt.Println("Starting https server...")
        err = http.ListenAndServeTLS(":443", config["certFile"].(string), config["keyFile"].(string), nil)
    } else {
        fmt.Println("Starting http server...")
        fmt.Println("[WARNING] SSL encryption disabled! This is not recommended, as securely logging in is not possible without it!")
        err = http.ListenAndServe(":80", nil)
    }

    if err != nil {
        panic(fmt.Sprintf("ListenAndServe: %s", err))
    }
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
    cookie, cerr := r.Cookie(pioneerAccessToken)
    valid, _ := cookieIsValid(cookie)
    if cerr == nil && valid {
        http.Redirect(w, r, "/main", http.StatusTemporaryRedirect)
        return
    }
    err := templateCollection.ExecuteTemplate(w, "login.html", nil)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
    cookie, err := r.Cookie(pioneerAccessToken)
    valid, token := cookieIsValid(cookie)
    if err != nil || !valid {
        http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
        return
    }

    terr := templateCollection.ExecuteTemplate(w, "main.html", templateModels[token.username])
    if terr != nil {
        http.Error(w, terr.Error(), http.StatusInternalServerError)
    }
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "You can only POST to this api!", http.StatusBadRequest)
        return
    }
    command := r.URL.Path[len("/api/"):]
    slashIndex := strings.Index(command, "/")
    if slashIndex != -1 {
        command = command[:slashIndex]
    }
    switch command {
    case "login":
        body, error := ioutil.ReadAll(r.Body)
        valid, username := isValidLogin(string(body))
        if error == nil && valid {
            u := uuid.NewV4()
            cookie := &http.Cookie{Name: pioneerAccessToken, Value: u.String(), Expires: time.Now().Add(30*time.Minute), Path: "/"}
            validTokens = append(validTokens, Token{cookie: cookie, username: username})
            http.SetCookie(w, cookie)
            fmt.Fprintln(w, u.String())
        } else {
            http.Error(w, "Wrong login!", 403)
        }

        break
    case "logout":
        cookie, err := r.Cookie(pioneerAccessToken)
        if err != nil {
            http.Error(w, "Unauthorized", 403)
            return
        }

        for i := 0; i < len(validTokens); i++ {
            if cookie.Value == validTokens[i].cookie.Value {
                validTokens[i].cookie.Expires = time.Now().Add(-1 * time.Minute)
            }
        }

        break
    case "getcmds":
        cookie, err := r.Cookie(pioneerAccessToken)
        valid, token := cookieIsValid(cookie)
        if err != nil || !valid {
            http.Error(w, "Unauthorized", 403)
            return
        }

        retval := "{"
        for i, cmd := range templateModels[token.username].Commands {
            if i > 0 {
                retval += ","
            }
            retval += "\"" + cmd.Name + "\": " + strconv.Itoa(cmd.ID)
        }
        retval += "}"

        fmt.Fprint(w, retval)

        break;
    case "cmd":
        cookie, err := r.Cookie(pioneerAccessToken)
        valid, token := cookieIsValid(cookie)
        if err != nil || !valid {
            http.Error(w, "Unauthorized", 403)
            return
        }

        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "Parameter error", 500)
            return
        }

        origCmd := r.URL.Path[len("/api/"):]
        if len(origCmd) < 5 {
            http.Error(w, "Parameter error", 500)
            return
        }

        id, serr := strconv.Atoi(origCmd[slashIndex + 1:])
        if serr != nil {
            http.Error(w, "Parameter error", 500)
            return
        }

        cmd, ok := commands.CommandsAvailable[id]
        if ok && in(cmd.AllowedUsers, token.username) {
            retval := cmd.ExecutableCommand.Execute(string(body))
            fmt.Fprint(w, retval)
            return
        }

        http.Error(w, "Command not found", 404)

        break
    }
}

func in(haystack []string, needle string) bool {
    for _, h := range haystack {
        if h == needle {
            return true
        }
    }

    return false
}

func getCommandsForUser(username string) []commands.DisplayCommand {
    var retval []commands.DisplayCommand
    for _, cmd := range commands.CommandsAvailable {
        if in(cmd.AllowedUsers, username) {
            retval = append(retval, cmd)
        }
    }
    return retval
}

func isValidLogin(body string) (bool, string) {
    nPos := strings.Index(body, "\n")
    if nPos < 0 || len(body) <= nPos + 1 {
        return false, ""
    }

    username := body[:nPos]
    password := body[nPos + 1:]

    expectedPass, ok := users[username]
    if !ok || expectedPass != password {
        return false, ""
    }

    return true, username
}

func cookieIsValid(cookie *http.Cookie) (bool,*Token) {
    if cookie == nil {
        return false,nil
    }
    for i := 0; i < len(validTokens); i++ {
        if time.Now().After(validTokens[i].cookie.Expires) {
            validTokens = append(validTokens[:i], validTokens[i+1:]...)
            i--
            continue
        }
        if cookie.Value == validTokens[i].cookie.Value {
            return true,&validTokens[i]
        }
    }

    return false,nil
}

func loadConfig() {
    var v interface{}
    f, _ := os.Open("config.json")
    r := JsonConfigReader.New(f)
    json.NewDecoder(r).Decode(&v)
    config = v.(map[string]interface{})

    users = make(map[string]string)
    u := config["users"].([]interface{})
    for _, us := range u {
        use := us.(map[string]interface{})
        users[use["username"].(string)] = use["password"].(string)
    }
}<|endoftext|>


<commit_before>package weed_server

import (
	"bytes"
	"code.google.com/p/weed-fs/go/glog"
	"code.google.com/p/weed-fs/go/topology"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/goraft/raft"
	"github.com/gorilla/mux"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type RaftServer struct {
	peers      []string // initial peers to join with
	raftServer raft.Server
	dataDir    string
	httpAddr   string
	version    string
	router     *mux.Router
	topo       *topology.Topology
}

func NewRaftServer(r *mux.Router, version string, peers []string, httpAddr string, dataDir string, topo *topology.Topology, pulseSeconds int) *RaftServer {
	s := &RaftServer{
		version:  version,
		peers:    peers,
		httpAddr: httpAddr,
		dataDir:  dataDir,
		router:   r,
		topo:     topo,
	}

	if glog.V(4) {
		raft.SetLogLevel(2)
	}

	raft.RegisterCommand(&topology.MaxVolumeIdCommand{})

	var err error
	transporter := raft.NewHTTPTransporter("/cluster")
	s.raftServer, err = raft.NewServer(s.httpAddr, s.dataDir, transporter, nil, topo, "")
	if err != nil {
		glog.V(0).Infoln(err)
		return nil
	}
	transporter.Install(s.raftServer, s)
	s.raftServer.SetHeartbeatInterval(1 * time.Second)
	s.raftServer.SetElectionTimeout(time.Duration(pulseSeconds) * 1150 * time.Millisecond)
	s.raftServer.Start()

	s.router.HandleFunc("/cluster/join", s.joinHandler).Methods("POST")
	s.router.HandleFunc("/cluster/status", s.statusHandler).Methods("GET")

	// Join to leader if specified.
	if len(s.peers) > 0 {
		glog.V(0).Infoln("Joining cluster:", strings.Join(s.peers, ","))

		if !s.raftServer.IsLogEmpty() {
			glog.V(0).Infoln("Cannot join with an existing log")
		} else {
			if err := s.Join(s.peers); err != nil {
				return nil
			}
			glog.V(0).Infoln("Joined cluster")
		}

		// Initialize the server by joining itself.
	} else if s.raftServer.IsLogEmpty() {
		glog.V(0).Infoln("Initializing new cluster")

		_, err := s.raftServer.Do(&raft.DefaultJoinCommand{
			Name:             s.raftServer.Name(),
			ConnectionString: "http://" + s.httpAddr,
		})

		if err != nil {
			glog.V(0).Infoln(err)
			return nil
		}

	} else {
		glog.V(0).Infoln("Recovered from log")
	}

	return s
}

func (s *RaftServer) Peers() (members []string) {
	peers := s.raftServer.Peers()

	for _, p := range peers {
		members = append(members, strings.TrimPrefix(p.ConnectionString, "http://"))
	}

	return
}

// Join joins an existing cluster.
func (s *RaftServer) Join(peers []string) error {
	command := &raft.DefaultJoinCommand{
		Name:             s.raftServer.Name(),
		ConnectionString: "http://" + s.httpAddr,
	}

	var b bytes.Buffer
	json.NewEncoder(&b).Encode(command)

	for _, m := range peers {
		target := fmt.Sprintf("http://%s/cluster/join", strings.TrimSpace(m))
		glog.V(0).Infoln("Attempting to connect to:", target)

		err := postFollowingOneRedirect(target, "application/json", &b)

		if err != nil {
			glog.V(0).Infoln("Post returned error: ", err.Error())
			if _, ok := err.(*url.Error); ok {
				// If we receive a network error try the next member
				continue
			}

			return err
		}

		return nil
	}

	return errors.New("Could not connect to any cluster peers")
}

// a workaround because http POST following redirection misses request body
func postFollowingOneRedirect(target string, contentType string, b *bytes.Buffer) error {
	backupReader := bytes.NewReader(b.Bytes())
	resp, err := http.Post(target, contentType, b)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	reply, _ := ioutil.ReadAll(resp.Body)
	statusCode := resp.StatusCode

	if statusCode == http.StatusMovedPermanently {
		var urlStr string
		if urlStr = resp.Header.Get("Location"); urlStr == "" {
			return errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
		}

		glog.V(0).Infoln("Post redirected to ", urlStr)
		resp2, err2 := http.Post(urlStr, contentType, backupReader)
		if err2 != nil {
			return err2
		}
		defer resp2.Body.Close()
		reply, _ = ioutil.ReadAll(resp2.Body)
		statusCode = resp2.StatusCode
	}

	glog.V(0).Infoln("Post returned status: ", statusCode, string(reply))
	if statusCode != http.StatusOK {
		return errors.New(string(reply))
	}

	return nil
}
<commit_msg>reusing http connections in raft server<commit_after>package weed_server

import (
	"bytes"
	"code.google.com/p/weed-fs/go/glog"
	"code.google.com/p/weed-fs/go/topology"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/goraft/raft"
	"github.com/gorilla/mux"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type RaftServer struct {
	peers      []string // initial peers to join with
	raftServer raft.Server
	dataDir    string
	httpAddr   string
	version    string
	router     *mux.Router
	topo       *topology.Topology
}

func NewRaftServer(r *mux.Router, version string, peers []string, httpAddr string, dataDir string, topo *topology.Topology, pulseSeconds int) *RaftServer {
	s := &RaftServer{
		version:  version,
		peers:    peers,
		httpAddr: httpAddr,
		dataDir:  dataDir,
		router:   r,
		topo:     topo,
	}

	if glog.V(4) {
		raft.SetLogLevel(2)
	}

	raft.RegisterCommand(&topology.MaxVolumeIdCommand{})

	var err error
	transporter := raft.NewHTTPTransporter("/cluster")
	transporter.Transport.MaxIdleConnsPerHost = 1024

	s.raftServer, err = raft.NewServer(s.httpAddr, s.dataDir, transporter, nil, topo, "")
	if err != nil {
		glog.V(0).Infoln(err)
		return nil
	}
	transporter.Install(s.raftServer, s)
	s.raftServer.SetHeartbeatInterval(1 * time.Second)
	s.raftServer.SetElectionTimeout(time.Duration(pulseSeconds) * 1150 * time.Millisecond)
	s.raftServer.Start()

	s.router.HandleFunc("/cluster/join", s.joinHandler).Methods("POST")
	s.router.HandleFunc("/cluster/status", s.statusHandler).Methods("GET")

	// Join to leader if specified.
	if len(s.peers) > 0 {
		glog.V(0).Infoln("Joining cluster:", strings.Join(s.peers, ","))

		if !s.raftServer.IsLogEmpty() {
			glog.V(0).Infoln("Cannot join with an existing log")
		} else {
			if err := s.Join(s.peers); err != nil {
				return nil
			}
			glog.V(0).Infoln("Joined cluster")
		}

		// Initialize the server by joining itself.
	} else if s.raftServer.IsLogEmpty() {
		glog.V(0).Infoln("Initializing new cluster")

		_, err := s.raftServer.Do(&raft.DefaultJoinCommand{
			Name:             s.raftServer.Name(),
			ConnectionString: "http://" + s.httpAddr,
		})

		if err != nil {
			glog.V(0).Infoln(err)
			return nil
		}

	} else {
		glog.V(0).Infoln("Recovered from log")
	}

	return s
}

func (s *RaftServer) Peers() (members []string) {
	peers := s.raftServer.Peers()

	for _, p := range peers {
		members = append(members, strings.TrimPrefix(p.ConnectionString, "http://"))
	}

	return
}

// Join joins an existing cluster.
func (s *RaftServer) Join(peers []string) error {
	command := &raft.DefaultJoinCommand{
		Name:             s.raftServer.Name(),
		ConnectionString: "http://" + s.httpAddr,
	}

	var b bytes.Buffer
	json.NewEncoder(&b).Encode(command)

	for _, m := range peers {
		target := fmt.Sprintf("http://%s/cluster/join", strings.TrimSpace(m))
		glog.V(0).Infoln("Attempting to connect to:", target)

		err := postFollowingOneRedirect(target, "application/json", &b)

		if err != nil {
			glog.V(0).Infoln("Post returned error: ", err.Error())
			if _, ok := err.(*url.Error); ok {
				// If we receive a network error try the next member
				continue
			}

			return err
		}

		return nil
	}

	return errors.New("Could not connect to any cluster peers")
}

// a workaround because http POST following redirection misses request body
func postFollowingOneRedirect(target string, contentType string, b *bytes.Buffer) error {
	backupReader := bytes.NewReader(b.Bytes())
	resp, err := http.Post(target, contentType, b)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	reply, _ := ioutil.ReadAll(resp.Body)
	statusCode := resp.StatusCode

	if statusCode == http.StatusMovedPermanently {
		var urlStr string
		if urlStr = resp.Header.Get("Location"); urlStr == "" {
			return errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
		}

		glog.V(0).Infoln("Post redirected to ", urlStr)
		resp2, err2 := http.Post(urlStr, contentType, backupReader)
		if err2 != nil {
			return err2
		}
		defer resp2.Body.Close()
		reply, _ = ioutil.ReadAll(resp2.Body)
		statusCode = resp2.StatusCode
	}

	glog.V(0).Infoln("Post returned status: ", statusCode, string(reply))
	if statusCode != http.StatusOK {
		return errors.New(string(reply))
	}

	return nil
}
<|endoftext|>


<commit_before>//
package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"operator": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false),
			},
			"values": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
	ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
	for _, raw := range ranges {
		data := raw.(map[string]interface{})
		ipRanges = append(ipRanges, &computeBeta.AliasIpRange{
			IpCidrRange:         data["ip_cidr_range"].(string),
			SubnetworkRangeName: data["subnetwork_range_name"].(string),
		})
	}
	return ipRanges
}

func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interface{} {
	rangesSchema := make([]map[string]interface{}, 0, len(ranges))
	for _, ipRange := range ranges {
		rangesSchema = append(rangesSchema, map[string]interface{}{
			"ip_cidr_range":         ipRange.IpCidrRange,
			"subnetwork_range_name": ipRange.SubnetworkRangeName,
		})
	}
	return rangesSchema
}

func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
	if v == nil {
		// We can't set default values for lists.
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	ls := v.([]interface{})
	if len(ls) == 0 {
		// We can't set default values for lists
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	if len(ls) > 1 || ls[0] == nil {
		return nil, fmt.Errorf("expected exactly one scheduling block")
	}

	original := ls[0].(map[string]interface{})
	scheduling := &computeBeta.Scheduling{
		ForceSendFields: make([]string, 0, 4),
	}

	if v, ok := original["automatic_restart"]; ok {
		scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
	}

	if v, ok := original["preemptible"]; ok {
		scheduling.Preemptible = v.(bool)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")
	}

	if v, ok := original["on_host_maintenance"]; ok {
		scheduling.OnHostMaintenance = v.(string)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
	}

	if v, ok := original["node_affinities"]; ok && v != nil {
		naSet := v.(*schema.Set).List()
		scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
		for _, nodeAffRaw := range naSet {
			if nodeAffRaw == nil {
				continue
			}
			nodeAff := nodeAffRaw.(map[string]interface{})
			transformed := &computeBeta.SchedulingNodeAffinity{
				Key:      nodeAff["key"].(string),
				Operator: nodeAff["operator"].(string),
				Values:   convertStringArr(nodeAff["values"].(*schema.Set).List()),
			}
			scheduling.NodeAffinities = append(scheduling.NodeAffinities, transformed)
		}
	}

	return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
	schedulingMap := map[string]interface{}{
		"on_host_maintenance": resp.OnHostMaintenance,
		"preemptible":         resp.Preemptible,
	}

	if resp.AutomaticRestart != nil {
		schedulingMap["automatic_restart"] = *resp.AutomaticRestart
	}

	nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
	for _, na := range resp.NodeAffinities {
		nodeAffinities.Add(map[string]interface{}{
			"key":      na.Key,
			"operator": na.Operator,
			"values":   schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
		})
	}
	schedulingMap["node_affinities"] = nodeAffinities

	return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
	flattened := make([]map[string]interface{}, len(accessConfigs))
	natIP := ""
	for i, ac := range accessConfigs {
		flattened[i] = map[string]interface{}{
			"nat_ip":       ac.NatIP,
			"network_tier": ac.NetworkTier,
		}
		if ac.SetPublicPtr {
			flattened[i]["public_ptr_domain_name"] = ac.PublicPtrDomainName
		}
		if natIP == "" {
			natIP = ac.NatIP
		}
	}
	return flattened, natIP
}

func flattenNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) {
	flattened := make([]map[string]interface{}, len(networkInterfaces))
	var region, internalIP, externalIP string

	for i, iface := range networkInterfaces {
		var ac []map[string]interface{}
		ac, externalIP = flattenAccessConfigs(iface.AccessConfigs)

		subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config)
		if err != nil {
			return nil, "", "", "", err
		}
		region = subnet.Region

		flattened[i] = map[string]interface{}{
			"network_ip":         iface.NetworkIP,
			"network":            ConvertSelfLinkToV1(iface.Network),
			"subnetwork":         ConvertSelfLinkToV1(iface.Subnetwork),
			"subnetwork_project": subnet.Project,
			"access_config":      ac,
			"alias_ip_range":     flattenAliasIpRange(iface.AliasIpRanges),
			"nic_type":           iface.NicType,
		}
		// Instance template interfaces never have names, so they're absent
		// in the instance template network_interface schema. We want to use the
		// same flattening code for both resource types, so we avoid trying to
		// set the name field when it's not set at the GCE end.
		if iface.Name != "" {
			flattened[i]["name"] = iface.Name
		}
		if internalIP == "" {
			internalIP = iface.NetworkIP
		}
	}
	return flattened, region, internalIP, externalIP, nil
}

func expandAccessConfigs(configs []interface{}) []*computeBeta.AccessConfig {
	acs := make([]*computeBeta.AccessConfig, len(configs))
	for i, raw := range configs {
		acs[i] = &computeBeta.AccessConfig{}
		acs[i].Type = "ONE_TO_ONE_NAT"
		if raw != nil {
			data := raw.(map[string]interface{})
			acs[i].NatIP = data["nat_ip"].(string)
			acs[i].NetworkTier = data["network_tier"].(string)
			if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" {
				acs[i].SetPublicPtr = true
				acs[i].PublicPtrDomainName = ptr.(string)
			}
		}
	}
	return acs
}

func expandNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) {
	configs := d.Get("network_interface").([]interface{})
	ifaces := make([]*computeBeta.NetworkInterface, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		network := data["network"].(string)
		subnetwork := data["subnetwork"].(string)
		if network == "" && subnetwork == "" {
			return nil, fmt.Errorf("exactly one of network or subnetwork must be provided")
		}

		nf, err := ParseNetworkFieldValue(network, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err)
		}

		subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i)
		sf, err := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err)
		}

		ifaces[i] = &computeBeta.NetworkInterface{
			NetworkIP:     data["network_ip"].(string),
			Network:       nf.RelativeLink(),
			Subnetwork:    sf.RelativeLink(),
			AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})),
			AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})),
			NicType:       data["nic_type"].(string),
		}
	}
	return ifaces, nil
}

func flattenServiceAccounts(serviceAccounts []*computeBeta.ServiceAccount) []map[string]interface{} {
	result := make([]map[string]interface{}, len(serviceAccounts))
	for i, serviceAccount := range serviceAccounts {
		result[i] = map[string]interface{}{
			"email":  serviceAccount.Email,
			"scopes": schema.NewSet(stringScopeHashcode, convertStringArrToInterface(serviceAccount.Scopes)),
		}
	}
	return result
}

func expandServiceAccounts(configs []interface{}) []*computeBeta.ServiceAccount {
	accounts := make([]*computeBeta.ServiceAccount, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		accounts[i] = &computeBeta.ServiceAccount{
			Email:  data["email"].(string),
			Scopes: canonicalizeServiceScopes(convertStringSet(data["scopes"].(*schema.Set))),
		}

		if accounts[i].Email == "" {
			accounts[i].Email = "default"
		}
	}
	return accounts
}

func flattenGuestAccelerators(accelerators []*computeBeta.AcceleratorConfig) []map[string]interface{} {
	acceleratorsSchema := make([]map[string]interface{}, len(accelerators))
	for i, accelerator := range accelerators {
		acceleratorsSchema[i] = map[string]interface{}{
			"count": accelerator.AcceleratorCount,
			"type":  accelerator.AcceleratorType,
		}
	}
	return acceleratorsSchema
}

func resourceInstanceTags(d TerraformResourceData) *computeBeta.Tags {
	// Calculate the tags
	var tags *computeBeta.Tags
	if v := d.Get("tags"); v != nil {
		vs := v.(*schema.Set)
		tags = new(computeBeta.Tags)
		tags.Items = make([]string, vs.Len())
		for i, v := range vs.List() {
			tags.Items[i] = v.(string)
		}

		tags.Fingerprint = d.Get("tags_fingerprint").(string)
	}

	return tags
}

func expandShieldedVmConfigs(d TerraformResourceData) *computeBeta.ShieldedInstanceConfig {
	if _, ok := d.GetOk("shielded_instance_config"); !ok {
		return nil
	}

	prefix := "shielded_instance_config.0"
	return &computeBeta.ShieldedInstanceConfig{
		EnableSecureBoot:          d.Get(prefix + ".enable_secure_boot").(bool),
		EnableVtpm:                d.Get(prefix + ".enable_vtpm").(bool),
		EnableIntegrityMonitoring: d.Get(prefix + ".enable_integrity_monitoring").(bool),
		ForceSendFields:           []string{"EnableSecureBoot", "EnableVtpm", "EnableIntegrityMonitoring"},
	}
}

func expandConfidentialInstanceConfig(d TerraformResourceData) *computeBeta.ConfidentialInstanceConfig {
	if _, ok := d.GetOk("confidential_instance_config"); !ok {
		return nil
	}

	prefix := "confidential_instance_config.0"
	return &computeBeta.ConfidentialInstanceConfig{
		EnableConfidentialCompute: d.Get(prefix + ".enable_confidential_compute").(bool),
		ForceSendFields:           []string{"EnableSecureBoot"},
	}
}

func flattenConfidentialInstanceConfig(ConfidentialInstanceConfig *computeBeta.ConfidentialInstanceConfig) []map[string]bool {
	if ConfidentialInstanceConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_confidential_compute": ConfidentialInstanceConfig.EnableConfidentialCompute,
	}}
}

func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedInstanceConfig) []map[string]bool {
	if shieldedVmConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_secure_boot":          shieldedVmConfig.EnableSecureBoot,
		"enable_vtpm":                 shieldedVmConfig.EnableVtpm,
		"enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring,
	}}
}

func expandDisplayDevice(d TerraformResourceData) *computeBeta.DisplayDevice {
	if _, ok := d.GetOk("enable_display"); !ok {
		return nil
	}
	return &computeBeta.DisplayDevice{
		EnableDisplay:   d.Get("enable_display").(bool),
		ForceSendFields: []string{"EnableDisplay"},
	}
}

func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} {
	if displayDevice == nil {
		return nil
	}

	return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
	if !d.HasChange("scheduling") {
		// This doesn't work correctly, which is why this method exists
		// But it is here for posterity
		return false
	}
	o, n := d.GetChange("scheduling")
	oScheduling := o.([]interface{})[0].(map[string]interface{})
	newScheduling := n.([]interface{})[0].(map[string]interface{})
	originalNa := oScheduling["node_affinities"].(*schema.Set)
	newNa := newScheduling["node_affinities"].(*schema.Set)
	if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
		return true
	}

	if oScheduling["preemptible"] != newScheduling["preemptible"] {
		return true
	}

	if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
		return true
	}

	return reflect.DeepEqual(newNa, originalNa)
}
<commit_msg>Fix min_node_cpus logic in GA (#4671) (#678)<commit_after>//
package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"operator": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false),
			},
			"values": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
	ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
	for _, raw := range ranges {
		data := raw.(map[string]interface{})
		ipRanges = append(ipRanges, &computeBeta.AliasIpRange{
			IpCidrRange:         data["ip_cidr_range"].(string),
			SubnetworkRangeName: data["subnetwork_range_name"].(string),
		})
	}
	return ipRanges
}

func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interface{} {
	rangesSchema := make([]map[string]interface{}, 0, len(ranges))
	for _, ipRange := range ranges {
		rangesSchema = append(rangesSchema, map[string]interface{}{
			"ip_cidr_range":         ipRange.IpCidrRange,
			"subnetwork_range_name": ipRange.SubnetworkRangeName,
		})
	}
	return rangesSchema
}

func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
	if v == nil {
		// We can't set default values for lists.
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	ls := v.([]interface{})
	if len(ls) == 0 {
		// We can't set default values for lists
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	if len(ls) > 1 || ls[0] == nil {
		return nil, fmt.Errorf("expected exactly one scheduling block")
	}

	original := ls[0].(map[string]interface{})
	scheduling := &computeBeta.Scheduling{
		ForceSendFields: make([]string, 0, 4),
	}

	if v, ok := original["automatic_restart"]; ok {
		scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
	}

	if v, ok := original["preemptible"]; ok {
		scheduling.Preemptible = v.(bool)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")
	}

	if v, ok := original["on_host_maintenance"]; ok {
		scheduling.OnHostMaintenance = v.(string)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
	}

	if v, ok := original["node_affinities"]; ok && v != nil {
		naSet := v.(*schema.Set).List()
		scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
		for _, nodeAffRaw := range naSet {
			if nodeAffRaw == nil {
				continue
			}
			nodeAff := nodeAffRaw.(map[string]interface{})
			transformed := &computeBeta.SchedulingNodeAffinity{
				Key:      nodeAff["key"].(string),
				Operator: nodeAff["operator"].(string),
				Values:   convertStringArr(nodeAff["values"].(*schema.Set).List()),
			}
			scheduling.NodeAffinities = append(scheduling.NodeAffinities, transformed)
		}
	}

	if v, ok := original["min_node_cpus"]; ok {
		scheduling.MinNodeCpus = int64(v.(int))
	}

	return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
	schedulingMap := map[string]interface{}{
		"on_host_maintenance": resp.OnHostMaintenance,
		"preemptible":         resp.Preemptible,
		"min_node_cpus":       resp.MinNodeCpus,
	}

	if resp.AutomaticRestart != nil {
		schedulingMap["automatic_restart"] = *resp.AutomaticRestart
	}

	nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
	for _, na := range resp.NodeAffinities {
		nodeAffinities.Add(map[string]interface{}{
			"key":      na.Key,
			"operator": na.Operator,
			"values":   schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
		})
	}
	schedulingMap["node_affinities"] = nodeAffinities

	return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
	flattened := make([]map[string]interface{}, len(accessConfigs))
	natIP := ""
	for i, ac := range accessConfigs {
		flattened[i] = map[string]interface{}{
			"nat_ip":       ac.NatIP,
			"network_tier": ac.NetworkTier,
		}
		if ac.SetPublicPtr {
			flattened[i]["public_ptr_domain_name"] = ac.PublicPtrDomainName
		}
		if natIP == "" {
			natIP = ac.NatIP
		}
	}
	return flattened, natIP
}

func flattenNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) {
	flattened := make([]map[string]interface{}, len(networkInterfaces))
	var region, internalIP, externalIP string

	for i, iface := range networkInterfaces {
		var ac []map[string]interface{}
		ac, externalIP = flattenAccessConfigs(iface.AccessConfigs)

		subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config)
		if err != nil {
			return nil, "", "", "", err
		}
		region = subnet.Region

		flattened[i] = map[string]interface{}{
			"network_ip":         iface.NetworkIP,
			"network":            ConvertSelfLinkToV1(iface.Network),
			"subnetwork":         ConvertSelfLinkToV1(iface.Subnetwork),
			"subnetwork_project": subnet.Project,
			"access_config":      ac,
			"alias_ip_range":     flattenAliasIpRange(iface.AliasIpRanges),
			"nic_type":           iface.NicType,
		}
		// Instance template interfaces never have names, so they're absent
		// in the instance template network_interface schema. We want to use the
		// same flattening code for both resource types, so we avoid trying to
		// set the name field when it's not set at the GCE end.
		if iface.Name != "" {
			flattened[i]["name"] = iface.Name
		}
		if internalIP == "" {
			internalIP = iface.NetworkIP
		}
	}
	return flattened, region, internalIP, externalIP, nil
}

func expandAccessConfigs(configs []interface{}) []*computeBeta.AccessConfig {
	acs := make([]*computeBeta.AccessConfig, len(configs))
	for i, raw := range configs {
		acs[i] = &computeBeta.AccessConfig{}
		acs[i].Type = "ONE_TO_ONE_NAT"
		if raw != nil {
			data := raw.(map[string]interface{})
			acs[i].NatIP = data["nat_ip"].(string)
			acs[i].NetworkTier = data["network_tier"].(string)
			if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" {
				acs[i].SetPublicPtr = true
				acs[i].PublicPtrDomainName = ptr.(string)
			}
		}
	}
	return acs
}

func expandNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) {
	configs := d.Get("network_interface").([]interface{})
	ifaces := make([]*computeBeta.NetworkInterface, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		network := data["network"].(string)
		subnetwork := data["subnetwork"].(string)
		if network == "" && subnetwork == "" {
			return nil, fmt.Errorf("exactly one of network or subnetwork must be provided")
		}

		nf, err := ParseNetworkFieldValue(network, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err)
		}

		subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i)
		sf, err := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err)
		}

		ifaces[i] = &computeBeta.NetworkInterface{
			NetworkIP:     data["network_ip"].(string),
			Network:       nf.RelativeLink(),
			Subnetwork:    sf.RelativeLink(),
			AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})),
			AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})),
			NicType:       data["nic_type"].(string),
		}
	}
	return ifaces, nil
}

func flattenServiceAccounts(serviceAccounts []*computeBeta.ServiceAccount) []map[string]interface{} {
	result := make([]map[string]interface{}, len(serviceAccounts))
	for i, serviceAccount := range serviceAccounts {
		result[i] = map[string]interface{}{
			"email":  serviceAccount.Email,
			"scopes": schema.NewSet(stringScopeHashcode, convertStringArrToInterface(serviceAccount.Scopes)),
		}
	}
	return result
}

func expandServiceAccounts(configs []interface{}) []*computeBeta.ServiceAccount {
	accounts := make([]*computeBeta.ServiceAccount, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		accounts[i] = &computeBeta.ServiceAccount{
			Email:  data["email"].(string),
			Scopes: canonicalizeServiceScopes(convertStringSet(data["scopes"].(*schema.Set))),
		}

		if accounts[i].Email == "" {
			accounts[i].Email = "default"
		}
	}
	return accounts
}

func flattenGuestAccelerators(accelerators []*computeBeta.AcceleratorConfig) []map[string]interface{} {
	acceleratorsSchema := make([]map[string]interface{}, len(accelerators))
	for i, accelerator := range accelerators {
		acceleratorsSchema[i] = map[string]interface{}{
			"count": accelerator.AcceleratorCount,
			"type":  accelerator.AcceleratorType,
		}
	}
	return acceleratorsSchema
}

func resourceInstanceTags(d TerraformResourceData) *computeBeta.Tags {
	// Calculate the tags
	var tags *computeBeta.Tags
	if v := d.Get("tags"); v != nil {
		vs := v.(*schema.Set)
		tags = new(computeBeta.Tags)
		tags.Items = make([]string, vs.Len())
		for i, v := range vs.List() {
			tags.Items[i] = v.(string)
		}

		tags.Fingerprint = d.Get("tags_fingerprint").(string)
	}

	return tags
}

func expandShieldedVmConfigs(d TerraformResourceData) *computeBeta.ShieldedInstanceConfig {
	if _, ok := d.GetOk("shielded_instance_config"); !ok {
		return nil
	}

	prefix := "shielded_instance_config.0"
	return &computeBeta.ShieldedInstanceConfig{
		EnableSecureBoot:          d.Get(prefix + ".enable_secure_boot").(bool),
		EnableVtpm:                d.Get(prefix + ".enable_vtpm").(bool),
		EnableIntegrityMonitoring: d.Get(prefix + ".enable_integrity_monitoring").(bool),
		ForceSendFields:           []string{"EnableSecureBoot", "EnableVtpm", "EnableIntegrityMonitoring"},
	}
}

func expandConfidentialInstanceConfig(d TerraformResourceData) *computeBeta.ConfidentialInstanceConfig {
	if _, ok := d.GetOk("confidential_instance_config"); !ok {
		return nil
	}

	prefix := "confidential_instance_config.0"
	return &computeBeta.ConfidentialInstanceConfig{
		EnableConfidentialCompute: d.Get(prefix + ".enable_confidential_compute").(bool),
		ForceSendFields:           []string{"EnableSecureBoot"},
	}
}

func flattenConfidentialInstanceConfig(ConfidentialInstanceConfig *computeBeta.ConfidentialInstanceConfig) []map[string]bool {
	if ConfidentialInstanceConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_confidential_compute": ConfidentialInstanceConfig.EnableConfidentialCompute,
	}}
}

func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedInstanceConfig) []map[string]bool {
	if shieldedVmConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_secure_boot":          shieldedVmConfig.EnableSecureBoot,
		"enable_vtpm":                 shieldedVmConfig.EnableVtpm,
		"enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring,
	}}
}

func expandDisplayDevice(d TerraformResourceData) *computeBeta.DisplayDevice {
	if _, ok := d.GetOk("enable_display"); !ok {
		return nil
	}
	return &computeBeta.DisplayDevice{
		EnableDisplay:   d.Get("enable_display").(bool),
		ForceSendFields: []string{"EnableDisplay"},
	}
}

func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} {
	if displayDevice == nil {
		return nil
	}

	return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
	if !d.HasChange("scheduling") {
		// This doesn't work correctly, which is why this method exists
		// But it is here for posterity
		return false
	}
	o, n := d.GetChange("scheduling")
	oScheduling := o.([]interface{})[0].(map[string]interface{})
	newScheduling := n.([]interface{})[0].(map[string]interface{})
	originalNa := oScheduling["node_affinities"].(*schema.Set)
	newNa := newScheduling["node_affinities"].(*schema.Set)
	if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
		return true
	}

	if oScheduling["preemptible"] != newScheduling["preemptible"] {
		return true
	}

	if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
		return true
	}

	if oScheduling["min_node_cpus"] != newScheduling["min_node_cpus"] {
		return true
	}

	return reflect.DeepEqual(newNa, originalNa)
}
<|endoftext|>


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

package present

import (
	"bufio"
	"bytes"
	"fmt"
	"html/template"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// Is the playground available?
var PlayEnabled = false

// TOOD(adg): replace the PlayEnabled flag with something less spaghetti-like.
// Instead this will probably be determined by a template execution Context
// value that contains various global metadata required when rendering
// templates.

func init() {
	Register("code", parseCode)
	Register("play", parseCode)
}

type Code struct {
	Text     template.HTML
	Play     bool   // runnable code
	FileName string // file name
	Ext      string // file extension
	Raw      []byte // content of the file
}

func (c Code) TemplateName() string { return "code" }

// The input line is a .code or .play entry with a file name and an optional HLfoo marker on the end.
// Anything between the file and HL (if any) is an address expression, which we treat as a string here.
// We pick off the HL first, for easy parsing.
var (
	highlightRE = regexp.MustCompile(`\s+HL([a-zA-Z0-9_]+)?$`)
	hlCommentRE = regexp.MustCompile(`(.+) // HL(.*)$`)
	codeRE      = regexp.MustCompile(`\.(code|play)\s+((?:(?:-edit|-numbers)\s+)*)([^\s]+)(?:\s+(.*))?$`)
)

// parseCode parses a code present directive. Its syntax:
//   .code [-numbers] [-edit] <filename> [address] [highlight]
// The directive may also be ".play" if the snippet is executable.
func parseCode(ctx *Context, sourceFile string, sourceLine int, cmd string) (Elem, error) {
	cmd = strings.TrimSpace(cmd)

	// Pull off the HL, if any, from the end of the input line.
	highlight := ""
	if hl := highlightRE.FindStringSubmatchIndex(cmd); len(hl) == 4 {
		highlight = cmd[hl[2]:hl[3]]
		cmd = cmd[:hl[2]-2]
	}

	// Parse the remaining command line.
	// Arguments:
	// args[0]: whole match
	// args[1]:  .code/.play
	// args[2]: flags ("-edit -numbers")
	// args[3]: file name
	// args[4]: optional address
	args := codeRE.FindStringSubmatch(cmd)
	if len(args) != 5 {
		return nil, fmt.Errorf("%s:%d: syntax error for .code/.play invocation", sourceFile, sourceLine)
	}
	command, flags, file, addr := args[1], args[2], args[3], strings.TrimSpace(args[4])
	play := command == "play" && PlayEnabled

	// Read in code file and (optionally) match address.
	filename := filepath.Join(filepath.Dir(sourceFile), file)
	textBytes, err := ctx.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}
	lo, hi, err := addrToByteRange(addr, 0, textBytes)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}

	// Acme pattern matches can stop mid-line,
	// so run to end of line in both directions if not at line start/end.
	for lo > 0 && textBytes[lo-1] != '\n' {
		lo--
	}
	if hi > 0 {
		for hi < len(textBytes) && textBytes[hi-1] != '\n' {
			hi++
		}
	}

	lines := codeLines(textBytes, lo, hi)

	for i, line := range lines {
		// Replace tabs by spaces, which work better in HTML.
		line.L = strings.Replace(line.L, "\t", "    ", -1)

		// Highlight lines that end with "// HL[highlight]"
		// and strip the magic comment.
		if m := hlCommentRE.FindStringSubmatch(line.L); m != nil {
			line.L = m[1]
			line.HL = m[2] == highlight
		}

		lines[i] = line
	}

	data := &codeTemplateData{
		Lines:   lines,
		Edit:    strings.Contains(flags, "-edit"),
		Numbers: strings.Contains(flags, "-numbers"),
	}

	// Include before and after in a hidden span for playground code.
	if play {
		data.Prefix = textBytes[:lo]
		data.Suffix = textBytes[hi:]
	}

	var buf bytes.Buffer
	if err := codeTemplate.Execute(&buf, data); err != nil {
		return nil, err
	}
	return Code{
		Text:     template.HTML(buf.String()),
		Play:     play,
		FileName: filepath.Base(filename),
		Ext:      filepath.Ext(filename),
		Raw:      textBytes,
	}, nil
}

type codeTemplateData struct {
	Lines          []codeLine
	Prefix, Suffix []byte
	Edit, Numbers  bool
}

var leadingSpaceRE = regexp.MustCompile(`^[ \t]*`)

var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{
	"trimSpace":    strings.TrimSpace,
	"leadingSpace": leadingSpaceRE.FindString,
}).Parse(codeTemplateHTML))

const codeTemplateHTML = `
{{with .Prefix}}
{{printf "%s" .}}
{{end}}

{{/*
	*/}}{{range .Lines}}{{/*
	*/}}{{if .HL}}{{leadingSpace .L}}{{trimSpace .L}}{{/*
	*/}}{{else}}{{.L}}{{end}}{{/*
*/}}
{{end}}

{{with .Suffix}}
{{printf "%s" .}}
{{end}}
`

// codeLine represents a line of code extracted from a source file.
type codeLine struct {
	L  string // The line of code.
	N  int    // The line number from the source file.
	HL bool   // Whether the line should be highlighted.
}

// codeLines takes a source file and returns the lines that
// span the byte range specified by start and end.
// It discards lines that end in "OMIT".
func codeLines(src []byte, start, end int) (lines []codeLine) {
	startLine := 1
	for i, b := range src {
		if i == start {
			break
		}
		if b == '\n' {
			startLine++
		}
	}
	s := bufio.NewScanner(bytes.NewReader(src[start:end]))
	for n := startLine; s.Scan(); n++ {
		l := s.Text()
		if strings.HasSuffix(l, "OMIT") {
			continue
		}
		lines = append(lines, codeLine{L: l, N: n})
	}
	// Trim leading and trailing blank lines.
	for len(lines) > 0 && len(lines[0].L) == 0 {
		lines = lines[1:]
	}
	for len(lines) > 0 && len(lines[len(lines)-1].L) == 0 {
		lines = lines[:len(lines)-1]
	}
	return
}

func parseArgs(name string, line int, args []string) (res []interface{}, err error) {
	res = make([]interface{}, len(args))
	for i, v := range args {
		if len(v) == 0 {
			return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
		}
		switch v[0] {
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			n, err := strconv.Atoi(v)
			if err != nil {
				return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
			}
			res[i] = n
		case '/':
			if len(v) < 2 || v[len(v)-1] != '/' {
				return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
			}
			res[i] = v
		case '$':
			res[i] = "$"
		default:
			return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
		}
	}
	return
}

// parseArg returns the integer or string value of the argument and tells which it is.
func parseArg(arg interface{}, max int) (ival int, sval string, isInt bool, err error) {
	switch n := arg.(type) {
	case int:
		if n <= 0 || n > max {
			return 0, "", false, fmt.Errorf("%d is out of range", n)
		}
		return n, "", true, nil
	case string:
		return 0, n, false, nil
	}
	return 0, "", false, fmt.Errorf("unrecognized argument %v type %T", arg, arg)
}

// match identifies the input line that matches the pattern in a code invocation.
// If start>0, match lines starting there rather than at the beginning.
// The return value is 1-indexed.
func match(file string, start int, lines []string, pattern string) (int, error) {
	// $ matches the end of the file.
	if pattern == "$" {
		if len(lines) == 0 {
			return 0, fmt.Errorf("%q: empty file", file)
		}
		return len(lines), nil
	}
	// /regexp/ matches the line that matches the regexp.
	if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
		re, err := regexp.Compile(pattern[1 : len(pattern)-1])
		if err != nil {
			return 0, err
		}
		for i := start; i < len(lines); i++ {
			if re.MatchString(lines[i]) {
				return i + 1, nil
			}
		}
		return 0, fmt.Errorf("%s: no match for %#q", file, pattern)
	}
	return 0, fmt.Errorf("unrecognized pattern: %q", pattern)
}
<commit_msg>go.tools/present: Remove formatting comments (HL, OMIT) from raw code.<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package present

import (
	"bufio"
	"bytes"
	"fmt"
	"html/template"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// Is the playground available?
var PlayEnabled = false

// TOOD(adg): replace the PlayEnabled flag with something less spaghetti-like.
// Instead this will probably be determined by a template execution Context
// value that contains various global metadata required when rendering
// templates.

func init() {
	Register("code", parseCode)
	Register("play", parseCode)
}

type Code struct {
	Text     template.HTML
	Play     bool   // runnable code
	FileName string // file name
	Ext      string // file extension
	Raw      []byte // content of the file
}

func (c Code) TemplateName() string { return "code" }

// The input line is a .code or .play entry with a file name and an optional HLfoo marker on the end.
// Anything between the file and HL (if any) is an address expression, which we treat as a string here.
// We pick off the HL first, for easy parsing.
var (
	highlightRE = regexp.MustCompile(`\s+HL([a-zA-Z0-9_]+)?$`)
	hlCommentRE = regexp.MustCompile(`(.+) // HL(.*)$`)
	codeRE      = regexp.MustCompile(`\.(code|play)\s+((?:(?:-edit|-numbers)\s+)*)([^\s]+)(?:\s+(.*))?$`)
)

// parseCode parses a code present directive. Its syntax:
//   .code [-numbers] [-edit] <filename> [address] [highlight]
// The directive may also be ".play" if the snippet is executable.
func parseCode(ctx *Context, sourceFile string, sourceLine int, cmd string) (Elem, error) {
	cmd = strings.TrimSpace(cmd)

	// Pull off the HL, if any, from the end of the input line.
	highlight := ""
	if hl := highlightRE.FindStringSubmatchIndex(cmd); len(hl) == 4 {
		highlight = cmd[hl[2]:hl[3]]
		cmd = cmd[:hl[2]-2]
	}

	// Parse the remaining command line.
	// Arguments:
	// args[0]: whole match
	// args[1]:  .code/.play
	// args[2]: flags ("-edit -numbers")
	// args[3]: file name
	// args[4]: optional address
	args := codeRE.FindStringSubmatch(cmd)
	if len(args) != 5 {
		return nil, fmt.Errorf("%s:%d: syntax error for .code/.play invocation", sourceFile, sourceLine)
	}
	command, flags, file, addr := args[1], args[2], args[3], strings.TrimSpace(args[4])
	play := command == "play" && PlayEnabled

	// Read in code file and (optionally) match address.
	filename := filepath.Join(filepath.Dir(sourceFile), file)
	textBytes, err := ctx.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}
	lo, hi, err := addrToByteRange(addr, 0, textBytes)
	if err != nil {
		return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err)
	}

	// Acme pattern matches can stop mid-line,
	// so run to end of line in both directions if not at line start/end.
	for lo > 0 && textBytes[lo-1] != '\n' {
		lo--
	}
	if hi > 0 {
		for hi < len(textBytes) && textBytes[hi-1] != '\n' {
			hi++
		}
	}

	lines := codeLines(textBytes, lo, hi)

	data := &codeTemplateData{
		Lines:   formatLines(lines, highlight),
		Edit:    strings.Contains(flags, "-edit"),
		Numbers: strings.Contains(flags, "-numbers"),
	}

	// Include before and after in a hidden span for playground code.
	if play {
		data.Prefix = textBytes[:lo]
		data.Suffix = textBytes[hi:]
	}

	var buf bytes.Buffer
	if err := codeTemplate.Execute(&buf, data); err != nil {
		return nil, err
	}
	return Code{
		Text:     template.HTML(buf.String()),
		Play:     play,
		FileName: filepath.Base(filename),
		Ext:      filepath.Ext(filename),
		Raw:      rawCode(lines),
	}, nil
}

// formatLines returns a new slice of codeLine with the given lines
// replacing tabs with spaces and adding highlighting where needed.
func formatLines(lines []codeLine, highlight string) []codeLine {
	formatted := make([]codeLine, len(lines))
	for i, line := range lines {
		// Replace tabs with spaces, which work better in HTML.
		line.L = strings.Replace(line.L, "\t", "    ", -1)

		// Highlight lines that end with "// HL[highlight]"
		// and strip the magic comment.
		if m := hlCommentRE.FindStringSubmatch(line.L); m != nil {
			line.L = m[1]
			line.HL = m[2] == highlight
		}

		formatted[i] = line
	}
	return formatted
}

// rawCode returns the code represented by the given codeLines without any kind
// of formatting.
func rawCode(lines []codeLine) []byte {
	b := new(bytes.Buffer)
	for _, line := range lines {
		b.WriteString(line.L)
		b.WriteByte('\n')
	}
	return b.Bytes()
}

type codeTemplateData struct {
	Lines          []codeLine
	Prefix, Suffix []byte
	Edit, Numbers  bool
}

var leadingSpaceRE = regexp.MustCompile(`^[ \t]*`)

var codeTemplate = template.Must(template.New("code").Funcs(template.FuncMap{
	"trimSpace":    strings.TrimSpace,
	"leadingSpace": leadingSpaceRE.FindString,
}).Parse(codeTemplateHTML))

const codeTemplateHTML = `
{{with .Prefix}}
{{printf "%s" .}}
{{end}}

{{/*
	*/}}{{range .Lines}}{{/*
	*/}}{{if .HL}}{{leadingSpace .L}}{{trimSpace .L}}{{/*
	*/}}{{else}}{{.L}}{{end}}{{/*
*/}}
{{end}}

{{with .Suffix}}
{{printf "%s" .}}
{{end}}
`

// codeLine represents a line of code extracted from a source file.
type codeLine struct {
	L  string // The line of code.
	N  int    // The line number from the source file.
	HL bool   // Whether the line should be highlighted.
}

// codeLines takes a source file and returns the lines that
// span the byte range specified by start and end.
// It discards lines that end in "OMIT".
func codeLines(src []byte, start, end int) (lines []codeLine) {
	startLine := 1
	for i, b := range src {
		if i == start {
			break
		}
		if b == '\n' {
			startLine++
		}
	}
	s := bufio.NewScanner(bytes.NewReader(src[start:end]))
	for n := startLine; s.Scan(); n++ {
		l := s.Text()
		if strings.HasSuffix(l, "OMIT") {
			continue
		}
		lines = append(lines, codeLine{L: l, N: n})
	}
	// Trim leading and trailing blank lines.
	for len(lines) > 0 && len(lines[0].L) == 0 {
		lines = lines[1:]
	}
	for len(lines) > 0 && len(lines[len(lines)-1].L) == 0 {
		lines = lines[:len(lines)-1]
	}
	return
}

func parseArgs(name string, line int, args []string) (res []interface{}, err error) {
	res = make([]interface{}, len(args))
	for i, v := range args {
		if len(v) == 0 {
			return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
		}
		switch v[0] {
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			n, err := strconv.Atoi(v)
			if err != nil {
				return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
			}
			res[i] = n
		case '/':
			if len(v) < 2 || v[len(v)-1] != '/' {
				return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
			}
			res[i] = v
		case '$':
			res[i] = "$"
		default:
			return nil, fmt.Errorf("%s:%d bad code argument %q", name, line, v)
		}
	}
	return
}

// parseArg returns the integer or string value of the argument and tells which it is.
func parseArg(arg interface{}, max int) (ival int, sval string, isInt bool, err error) {
	switch n := arg.(type) {
	case int:
		if n <= 0 || n > max {
			return 0, "", false, fmt.Errorf("%d is out of range", n)
		}
		return n, "", true, nil
	case string:
		return 0, n, false, nil
	}
	return 0, "", false, fmt.Errorf("unrecognized argument %v type %T", arg, arg)
}

// match identifies the input line that matches the pattern in a code invocation.
// If start>0, match lines starting there rather than at the beginning.
// The return value is 1-indexed.
func match(file string, start int, lines []string, pattern string) (int, error) {
	// $ matches the end of the file.
	if pattern == "$" {
		if len(lines) == 0 {
			return 0, fmt.Errorf("%q: empty file", file)
		}
		return len(lines), nil
	}
	// /regexp/ matches the line that matches the regexp.
	if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
		re, err := regexp.Compile(pattern[1 : len(pattern)-1])
		if err != nil {
			return 0, err
		}
		for i := start; i < len(lines); i++ {
			if re.MatchString(lines[i]) {
				return i + 1, nil
			}
		}
		return 0, fmt.Errorf("%s: no match for %#q", file, pattern)
	}
	return 0, fmt.Errorf("unrecognized pattern: %q", pattern)
}
<|endoftext|>


<commit_before>package edsm

import (
	"regexp"
)

const (
	DISTANCE_COMMAND_REGEX = `^(.*)\s?\/\s?(.*)`
	SPHERE_COMMAND_REGEX   = `^(.*)\s+?(\d*\.?\d*)`
)

// Regex that matches commands like:
// "sothis / bava".
// Two capture groups are returned  (.*), (sothis), (bava)
func RegexMatchDistanceCommand(msg string) []string {
	placesPattern := regexp.MustCompile(DISTANCE_COMMAND_REGEX)
	return placesPattern.FindStringSubmatch(msg)
}

// Regex that matches commands like:
// "sothis 14.33ly".
// Two capture groups are returned  (.*), (sothis), (14.33)
func RegexMatchSphereCommand(msg string) []string {
	placesPattern := regexp.MustCompile(SPHERE_COMMAND_REGEX)
	return placesPattern.FindStringSubmatch(msg)
}
<commit_msg>changing visibility of variables<commit_after>package edsm

import (
	"regexp"
)

const (
	distance_command_regex		= `^(.*)\s?\/\s?(.*)`
	sphere_command_regex		= `^(.*)\s+?(\d*\.?\d*)`
)

// Regex that matches commands like:
// "sothis / bava".
// Two capture groups are returned  (.*), (sothis), (bava)
func RegexMatchDistanceCommand(msg string) []string {
	placesPattern := regexp.MustCompile(distance_command_regex)
	return placesPattern.FindStringSubmatch(msg)
}

// Regex that matches commands like:
// "sothis 14.33ly".
// Two capture groups are returned  (.*), (sothis), (14.33)
func RegexMatchSphereCommand(msg string) []string {
	placesPattern := regexp.MustCompile(sphere_command_regex)
	return placesPattern.FindStringSubmatch(msg)
}
<|endoftext|>


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

package autocmd

import (
	"context"
	"sync"

	"github.com/neovim/go-client/nvim"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/zchee/nvim-go/src/buildctx"
	"github.com/zchee/nvim-go/src/command"
	"github.com/zchee/nvim-go/src/logger"
	"golang.org/x/sync/syncmap"
)

// Autocmd represents a autocmd context.
type Autocmd struct {
	ctx    context.Context
	cancel context.CancelFunc

	Nvim         *nvim.Nvim
	buildContext *buildctx.Context
	cmd          *command.Command

	bufWritePostChan chan error
	bufWritePreChan  chan interface{}
	mu               sync.Mutex
	wg               sync.WaitGroup

	errs *syncmap.Map
}

// Register registers autocmd to Neovim.
func Register(pctx context.Context, p *plugin.Plugin, buildContext *buildctx.Context, cmd *command.Command) {
	ctx, cancel := context.WithCancel(pctx)
	ctx = logger.NewContext(ctx, logger.FromContext(ctx).Named("autocmd"))

	autocmd := &Autocmd{
		ctx:              ctx,
		cancel:           cancel,
		Nvim:             p.Nvim,
		buildContext:     buildContext,
		cmd:              cmd,
		bufWritePreChan:  make(chan interface{}),
		bufWritePostChan: make(chan error),
		errs:             new(syncmap.Map),
	}

	logger.FromContext(ctx).Info("Register")

	// Handle the open the file.
	// If open the file at first, run the 'BufEnter' -> 'VimEnter'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufEnter", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.BufEnter)

	// Handle the initial start Neovim process.
	// Note that does not run the 'VimEnter' handler if open the *not* go file. Because 'VimEnter' handler already run the other file or directory.
	// TODO(zchee): consider Pattern to '*' instead of '*.go' with get '&filetype' and early return
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "VimEnter", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.VimEnter)

	// Handle the before the read to file.
	// If create the new file, does not run the 'BufReadPre', Instead of 'BufNewFile'.
	// p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufReadPre", Group: "nvim-go-autocmd", Pattern: "*.go"}, autocmd.BufReadPre)

	// Handle create the new file.
	// p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufNewFile", Group: "nvim-go-autocmd", Pattern: "*.go"}, autocmd.BufReadPre)

	// Handle the before the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePre", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.bufWritePre)

	// Handle the after the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePost", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.bufWritePost)
}
<commit_msg>autocmd: add missing 'Eval' option to BufReadPre and BufNewFile<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package autocmd

import (
	"context"
	"sync"

	"github.com/neovim/go-client/nvim"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/zchee/nvim-go/src/buildctx"
	"github.com/zchee/nvim-go/src/command"
	"github.com/zchee/nvim-go/src/logger"
	"golang.org/x/sync/syncmap"
)

// Autocmd represents a autocmd context.
type Autocmd struct {
	ctx    context.Context
	cancel context.CancelFunc

	Nvim         *nvim.Nvim
	buildContext *buildctx.Context
	cmd          *command.Command

	bufWritePostChan chan error
	bufWritePreChan  chan interface{}
	mu               sync.Mutex
	wg               sync.WaitGroup

	errs *syncmap.Map
}

// Register registers autocmd to Neovim.
func Register(pctx context.Context, p *plugin.Plugin, buildContext *buildctx.Context, cmd *command.Command) {
	ctx, cancel := context.WithCancel(pctx)
	ctx = logger.NewContext(ctx, logger.FromContext(ctx).Named("autocmd"))

	autocmd := &Autocmd{
		ctx:              ctx,
		cancel:           cancel,
		Nvim:             p.Nvim,
		buildContext:     buildContext,
		cmd:              cmd,
		bufWritePreChan:  make(chan interface{}),
		bufWritePostChan: make(chan error),
		errs:             new(syncmap.Map),
	}

	logger.FromContext(ctx).Info("Register")

	// Handle the open the file.
	// If open the file at first, run the 'BufEnter' -> 'VimEnter'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufEnter", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.BufEnter)

	// Handle the initial start Neovim process.
	// Note that does not run the 'VimEnter' handler if open the *not* go file. Because 'VimEnter' handler already run the other file or directory.
	// TODO(zchee): consider Pattern to '*' instead of '*.go' with get '&filetype' and early return
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "VimEnter", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.VimEnter)

	// Handle the before the read to file.
	// If create the new file, does not run the 'BufReadPre', Instead of 'BufNewFile'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufReadPre", Group: "nvim-go-autocmd", Pattern: "*.go", Eval: "*"}, autocmd.BufReadPre)

	// Handle create the new file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufNewFile", Group: "nvim-go-autocmd", Pattern: "*.go", Eval: "*"}, autocmd.BufReadPre)

	// Handle the before the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePre", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.bufWritePre)

	// Handle the after the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePost", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.bufWritePost)
}
<|endoftext|>


<commit_before>package croc

import (
	"bufio"
	"fmt"
	"os"
	"strings"

	humanize "github.com/dustin/go-humanize"
)

func promptCodePhrase() string {
	return getInput("Enter receive code: ")
}

func promptOkayToRecieve(f FileMetaData) (ok bool) {
	fileOrFolder := "file"
	if f.IsDir {
		fileOrFolder = "folder"
	}
	return "y" == getInput(fmt.Sprintf(
		`Receiving %s (%s) into: %s
ok? (y/N): `,
		fileOrFolder,
		humanize.Bytes(uint64(f.Size)),
		f.Name,
	))
}

func showIntro(code string, f FileMetaData) {
	fileOrFolder := "file"
	if f.IsDir {
		fileOrFolder = "folder"
	}
	fmt.Fprintf(os.Stderr,
		`Sending %s %s named '%s'
Code is: %s
On the other computer, please run:

croc %s
`,
		humanize.Bytes(uint64(f.Size)),
		fileOrFolder,
		f.Name,
		code,
		code,
	)
}

func getInput(prompt string) string {
	reader := bufio.NewReader(os.Stdin)
	fmt.Fprintf(os.Stderr, "%s", prompt)
	text, _ := reader.ReadString('\n')
	return strings.TrimSpace(text)
}
<commit_msg>fix bug<commit_after>package croc

import (
	"bufio"
	"fmt"
	"os"
	"strings"

	humanize "github.com/dustin/go-humanize"
)

func promptCodePhrase() string {
	return getInput("Enter receive code: ")
}

func promptOkayToRecieve(f FileMetaData) (ok bool) {
	overwritingOrReceiving := "Receiving"
	if exists(f.Name) {
		overwritingOrReceiving = "Overwriting"
	}
	fileOrFolder := "file"
	if f.IsDir {
		fileOrFolder = "folder"
	}
	return "y" == getInput(fmt.Sprintf(
		`%s %s (%s) into: %s
ok? (y/N): `,
		overwritingOrReceiving,
		fileOrFolder,
		humanize.Bytes(uint64(f.Size)),
		f.Name,
	))
}

func showIntro(code string, f FileMetaData) {
	fileOrFolder := "file"
	if f.IsDir {
		fileOrFolder = "folder"
	}
	fmt.Fprintf(os.Stderr,
		`Sending %s %s named '%s'
Code is: %s
On the other computer, please run:

croc %s
`,
		humanize.Bytes(uint64(f.Size)),
		fileOrFolder,
		f.Name,
		code,
		code,
	)
}

func getInput(prompt string) string {
	reader := bufio.NewReader(os.Stdin)
	fmt.Fprintf(os.Stderr, "%s", prompt)
	text, _ := reader.ReadString('\n')
	return strings.TrimSpace(text)
}
<|endoftext|>


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

package main

import (
	"fmt"
	"github.com/couchbaselabs/dparval"
	"github.com/couchbaselabs/indexing/api"
	ast "github.com/couchbaselabs/tuqtng/ast"
	"github.com/prataprc/go-couchbase"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
)

type BucketWorkerCmd struct {
	client     couchbase.Client
	pool       *couchbase.Pool
	bucketname string
	bmeta      *bucketMeta
	nconn      int
	rpcurl     string
}

type BucketWorker struct {
	client     couchbase.Client            // couchbase client
	pool       *couchbase.Pool             // pool where the bucket lives
	bucketname string                      // bucket for which to open the feed
	bmeta      *bucketMeta                 // bucket meta-data
	mclients   map[string][]*indexerClient // rpc clients per index
}

type indexerClient struct {
	client *rpc.Client
	mch    chan *api.Mutation
}

func NewBucketWorker(bwc BucketWorkerCmd) *BucketWorker {
	var rpcconn net.Conn
	var err error

	mclients := make(map[string][]*indexerClient)
	for uuid, _ := range bwc.bmeta.indexMap {
		iclients := make([]*indexerClient, 0, bwc.nconn)
		for i := 0; i < bwc.nconn; i++ {
			if rpcconn, err = net.Dial("tcp", bwc.rpcurl); err != nil {
				log.Printf(
					"error connecting with mutation server %v: %v\n", bwc.rpcurl, err)
				return nil
			}
			s := &indexerClient{
				client: jsonrpc.NewClient(rpcconn),
				mch:    make(chan *api.Mutation, 1000),
			}
			iclients = append(iclients, s)
		}
		mclients[uuid] = iclients
	}
	return &BucketWorker{
		client:     bwc.client,
		pool:       bwc.pool,
		bucketname: bwc.bucketname,
		bmeta:      bwc.bmeta,
		mclients:   mclients,
	}
}

func push2Indexer(s *indexerClient, quit chan interface{}, kill chan bool) {
	var r bool
	var err error

loop:
	for {
		select {
		case m, ok := <-s.mch:
			if ok {
				err = s.client.Call(PROCESS_1MUTATION, *m, &r)
			}
		case <-kill:
			break loop
		}
		if err != nil {
			quit <- ExitRoutine{kill, err}
			break
		}
	}
}

func (bw *BucketWorker) run(quit chan interface{}, kill chan bool) {
	var err error
	var pool couchbase.Pool
	var bucket *couchbase.Bucket

	finish := func() {
		if bucket != nil {
			bucket.Close()
		}
	}

	tryConnection(func() bool {
		// Refresh the pool to get any new buckets created on the server.
		if pool, err = bw.client.GetPool("default"); err != nil {
			fmt.Println("Error getting pool", err)
			finish()
			return false
		}
		bw.pool = &pool
		// Get bucket instance
		if bucket, err = bw.pool.GetBucket(bw.bucketname); err != nil {
			log.Printf("Unable to get bucket %v\n", bw.bucketname)
			finish()
			return false
		}
		return true
	})

	// Open feed
	bfeed := NewUprStreams(bucket)
	if err := bfeed.openFeed(bw.bmeta.vector); err != nil {
		log.Printf("Unable to open feed for bucket %v: %v\n", bw.bucketname, err)
		finish()
		return
	}

	count := 0
loop:
	for {
		select {
		case e, ok := <-bfeed.feed.C:
			if ok == false {
				quit <- ExitRoutine{kill, nil}
				break loop
			}
			for uuid, astexprs := range bw.bmeta.indexExprs {
				ii := bw.bmeta.indexMap[uuid]
				m := api.Mutation{
					Type:    api.UprEventName(e.Opstr),
					Indexid: uuid,
					Docid:   string(e.Key),
					Vbucket: e.Vbucket,
					Seqno:   e.Seqno,
				}
				if ii.IsPrimary && m.Type == api.INSERT {
					m.SecondaryKey = [][]byte{e.Key}
				} else if m.Type == api.INSERT {
					m.SecondaryKey = evaluate(e.Value, astexprs)
				}
				//log.Println(e.Opstr, e.Seqno, uuid[:8], bw.bucketname, m.Docid, fmtSKey(m.SecondaryKey))
				x := count % len(bw.mclients[uuid])
				bw.mclients[uuid][x].mch <- &m
				count++
			}
		case <-kill:
			break loop
		}
	}
	bfeed.closeFeed()
	// Close all connections with indexer.
	for _, sl := range bw.mclients {
		for _, s := range sl {
			close(s.mch)
		}
	}
}

func evaluate(value []byte, astexprs []ast.Expression) [][]byte {
	secKey := make([][]byte, 0)
	for _, expr := range astexprs {
		key, err := expr.Evaluate(dparval.NewValueFromBytes([]byte(value)))
		if err != nil {
			log.Println(err)
			secKey = append(secKey, []byte{})
		} else {
			secKey = append(secKey, key.Bytes())
		}
	}
	return secKey
}

func fmtSKey(keys [][]byte) []string {
	ss := make([]string, 0)
	for _, bs := range keys {
		ss = append(ss, string(bs))
	}
	return ss
}
<commit_msg>connections are distributed based on Vbucket.<commit_after>//  Copyright (c) 2013 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package main

import (
	"fmt"
	"github.com/couchbaselabs/dparval"
	"github.com/couchbaselabs/indexing/api"
	ast "github.com/couchbaselabs/tuqtng/ast"
	"github.com/prataprc/go-couchbase"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
)

type BucketWorkerCmd struct {
	client     couchbase.Client
	pool       *couchbase.Pool
	bucketname string
	bmeta      *bucketMeta
	nconn      int
	rpcurl     string
}

type BucketWorker struct {
	client     couchbase.Client            // couchbase client
	pool       *couchbase.Pool             // pool where the bucket lives
	bucketname string                      // bucket for which to open the feed
	bmeta      *bucketMeta                 // bucket meta-data
	mclients   map[string][]*indexerClient // rpc clients per index
}

type indexerClient struct {
	client *rpc.Client
	mch    chan *api.Mutation
}

func NewBucketWorker(bwc BucketWorkerCmd) *BucketWorker {
	var rpcconn net.Conn
	var err error

	mclients := make(map[string][]*indexerClient)
	for uuid, _ := range bwc.bmeta.indexMap {
		iclients := make([]*indexerClient, 0, bwc.nconn)
		for i := 0; i < bwc.nconn; i++ {
			if rpcconn, err = net.Dial("tcp", bwc.rpcurl); err != nil {
				log.Printf(
					"error connecting with mutation server %v: %v\n", bwc.rpcurl, err)
				return nil
			}
			s := &indexerClient{
				client: jsonrpc.NewClient(rpcconn),
				mch:    make(chan *api.Mutation, 1000),
			}
			iclients = append(iclients, s)
		}
		mclients[uuid] = iclients
	}
	return &BucketWorker{
		client:     bwc.client,
		pool:       bwc.pool,
		bucketname: bwc.bucketname,
		bmeta:      bwc.bmeta,
		mclients:   mclients,
	}
}

func push2Indexer(s *indexerClient, quit chan interface{}, kill chan bool) {
	var r bool
	var err error

loop:
	for {
		select {
		case m, ok := <-s.mch:
			if ok {
				err = s.client.Call(PROCESS_1MUTATION, *m, &r)
			}
		case <-kill:
			break loop
		}
		if err != nil {
			quit <- ExitRoutine{kill, err}
			break
		}
	}
}

func (bw *BucketWorker) run(quit chan interface{}, kill chan bool) {
	var err error
	var pool couchbase.Pool
	var bucket *couchbase.Bucket

	finish := func() {
		if bucket != nil {
			bucket.Close()
		}
	}

	tryConnection(func() bool {
		// Refresh the pool to get any new buckets created on the server.
		if pool, err = bw.client.GetPool("default"); err != nil {
			fmt.Println("Error getting pool", err)
			finish()
			return false
		}
		bw.pool = &pool
		// Get bucket instance
		if bucket, err = bw.pool.GetBucket(bw.bucketname); err != nil {
			log.Printf("Unable to get bucket %v\n", bw.bucketname)
			finish()
			return false
		}
		return true
	})

	// Open feed
	bfeed := NewUprStreams(bucket)
	if err := bfeed.openFeed(bw.bmeta.vector); err != nil {
		log.Printf("Unable to open feed for bucket %v: %v\n", bw.bucketname, err)
		finish()
		return
	}

	count := 0
loop:
	for {
		select {
		case e, ok := <-bfeed.feed.C:
			if ok == false {
				quit <- ExitRoutine{kill, nil}
				break loop
			}
			for uuid, astexprs := range bw.bmeta.indexExprs {
				ii := bw.bmeta.indexMap[uuid]
				m := api.Mutation{
					Type:    api.UprEventName(e.Opstr),
					Indexid: uuid,
					Docid:   string(e.Key),
					Vbucket: e.Vbucket,
					Seqno:   e.Seqno,
				}
				if ii.IsPrimary && m.Type == api.INSERT {
					m.SecondaryKey = [][]byte{e.Key}
				} else if m.Type == api.INSERT {
					m.SecondaryKey = evaluate(e.Value, astexprs)
				}
				//log.Println(e.Opstr, e.Seqno, uuid[:8], bw.bucketname, m.Docid, fmtSKey(m.SecondaryKey))
				x := int(e.Vbucket) % len(bw.mclients[uuid])
				bw.mclients[uuid][x].mch <- &m
				count++
			}
		case <-kill:
			break loop
		}
	}
	bfeed.closeFeed()
	// Close all connections with indexer.
	for _, sl := range bw.mclients {
		for _, s := range sl {
			close(s.mch)
		}
	}
}

func evaluate(value []byte, astexprs []ast.Expression) [][]byte {
	secKey := make([][]byte, 0)
	for _, expr := range astexprs {
		key, err := expr.Evaluate(dparval.NewValueFromBytes([]byte(value)))
		if err != nil {
			log.Println(err)
			secKey = append(secKey, []byte{})
		} else {
			secKey = append(secKey, key.Bytes())
		}
	}
	return secKey
}

func fmtSKey(keys [][]byte) []string {
	ss := make([]string, 0)
	for _, bs := range keys {
		ss = append(ss, string(bs))
	}
	return ss
}
<|endoftext|>


<commit_before>package app

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"runtime/debug"
	text_template "text/template"

	"appengine"
	"securecookie"
)

type PageData struct {
	FullName string
	Message  string
	Title    template.HTML
	Css      template.HTML
	Body     template.HTML
	Js       template.HTML
}

var tmpl = template.Must(template.ParseGlob("templates/*.html"))
var text_tmpl = text_template.Must(text_template.ParseGlob("templates/*.txt"))

func fillPageData(name string, data interface{}, pd *PageData) error {
	// Body is required, unlike title, css, and js.
	html, err := ExecuteTemplate(name+"-body", data)
	if err != nil {
		return err
	}
	pd.Body = html

	maybeExecuteSubTemplate := func(subTemplateName string, field *template.HTML) error {
		fullName := name + "-" + subTemplateName
		if tmpl.Lookup(fullName) == nil {
			return nil
		}
		if html, err = ExecuteTemplate(fullName, data); err != nil {
			return err
		}
		*field = html
		return nil
	}

	if err = maybeExecuteSubTemplate("title", &pd.Title); err != nil {
		return err
	}
	if err = maybeExecuteSubTemplate("css", &pd.Css); err != nil {
		return err
	}
	if err = maybeExecuteSubTemplate("js", &pd.Js); err != nil {
		return err
	}

	return nil
}

func setContentTypeUtf8(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
}

func RedirectWithMessage(w http.ResponseWriter, r *http.Request, url, msg string) {
	SetFlash(msg, w)
	http.Redirect(w, r, url, http.StatusSeeOther)
}

func RenderPageOrDie(w http.ResponseWriter, c *Context, name string, data interface{}) {
	fullName := ""
	if c.LoggedIn() {
		fullName = c.Session().FullName
	}

	pd := &PageData{}
	if err := fillPageData(name, data, pd); err != nil {
		ServeError(w, err)
		return
	}
	pd.FullName = fullName
	pd.Message = c.Flash()

	setContentTypeUtf8(w)
	if err := tmpl.ExecuteTemplate(w, "base.html", pd); err != nil {
		ServeError(w, err)
	}
}

func RenderTemplateOrDie(w http.ResponseWriter, name string, data interface{}) {
	setContentTypeUtf8(w)
	if err := tmpl.ExecuteTemplate(w, name, data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// If the returned error is not nil, it is guaranteed to have type
// template.Error.
func ExecuteTemplate(name string, data interface{}) (template.HTML, error) {
	buf := &bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(buf, name, data); err != nil {
		return "", err
	}
	return template.HTML(buf.String()), nil
}

func ExecuteTextTemplate(name string, data interface{}) (string, error) {
	buf := &bytes.Buffer{}
	if err := text_tmpl.ExecuteTemplate(buf, name, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func ServeInfo(w http.ResponseWriter, info string) {
	setContentTypeUtf8(w)
	w.Write([]byte(info))
}

func Serve404(w http.ResponseWriter) {
	http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}

func ServeError(w http.ResponseWriter, data interface{}) {
	http.Error(w, fmt.Sprint(data), http.StatusInternalServerError)
}

type AppHandlerFunc func(http.ResponseWriter, *http.Request, *Context)

// Wraps other http handlers. Creates context object, recovers from panics, etc.
func WrapHandlerImpl(fn AppHandlerFunc, parseForm bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		c := &Context{}

		// See http://blog.golang.org/2010/08/defer-panic-and-recover.html.
		defer func() {
			if data := recover(); data != nil {
				c.Aec().Errorf(fmt.Sprintf("%v", data))
				ServeError(w, data)
			}
		}()

		// Initialize the request context object.
		c.SetAec(appengine.NewContext(r))
		CheckError(ReadSession(r, c))
		if msg, err := ConsumeFlash(w, r); err != nil && err != http.ErrNoCookie {
			ServeError(w, err)
			return
		} else {
			c.SetFlash(msg)
		}

		if parseForm {
			CheckError(r.ParseForm())
		}
		fn(w, r, c)
	}
}

func WrapHandler(fn AppHandlerFunc) http.HandlerFunc {
	return WrapHandlerImpl(fn, true)
}

func WrapHandlerNoParseForm(fn AppHandlerFunc) http.HandlerFunc {
	return WrapHandlerImpl(fn, false)
}

type ErrorWithStackTrace struct {
	Stack []byte // from debug.Stack()
	Err   error
}

func (e *ErrorWithStackTrace) Error() string {
	if kPrintStackTrace {
		return fmt.Sprintf("%s\n%v", e.Stack, e.Err)
	}
	return fmt.Sprint(e.Err)
}

func CheckError(err error, v ...interface{}) {
	if err != nil {
		if len(v) > 0 {
			err = errors.New(fmt.Sprintf("%v\n%v", err, v))
		}
		e := &ErrorWithStackTrace{
			Stack: debug.Stack(),
			Err:   err,
		}
		panic(e)
	}
}

func Assert(condition bool, v ...interface{}) {
	if !condition {
		e := &ErrorWithStackTrace{
			Stack: debug.Stack(),
			Err:   errors.New(fmt.Sprint(v...)),
		}
		panic(e)
	}
}

func GenerateSecureRandomString() []byte {
	return securecookie.GenerateRandomKey(32)
}

func SaltAndHash(salt []byte, password string) []byte {
	h := sha1.New()
	io.WriteString(h, string(salt))
	io.WriteString(h, password)
	return h.Sum(nil)
}

func AppHostname(c *Context) string {
	if kAppHostname != "" {
		return kAppHostname
	}
	return appengine.DefaultVersionHostname(c.Aec())
}

func AppHostnameForPayPal(c *Context) string {
	if kAppHostnameForPayPal != "" {
		return kAppHostnameForPayPal
	}
	return AppHostname(c)
}

func ContainsString(slice []string, str string) bool {
	for _, v := range slice {
		if str == v {
			return true
		}
	}
	return false
}
<commit_msg>simplify recover logging<commit_after>package app

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"runtime/debug"
	text_template "text/template"

	"appengine"
	"securecookie"
)

type PageData struct {
	FullName string
	Message  string
	Title    template.HTML
	Css      template.HTML
	Body     template.HTML
	Js       template.HTML
}

var tmpl = template.Must(template.ParseGlob("templates/*.html"))
var text_tmpl = text_template.Must(text_template.ParseGlob("templates/*.txt"))

func fillPageData(name string, data interface{}, pd *PageData) error {
	// Body is required, unlike title, css, and js.
	html, err := ExecuteTemplate(name+"-body", data)
	if err != nil {
		return err
	}
	pd.Body = html

	maybeExecuteSubTemplate := func(subTemplateName string, field *template.HTML) error {
		fullName := name + "-" + subTemplateName
		if tmpl.Lookup(fullName) == nil {
			return nil
		}
		if html, err = ExecuteTemplate(fullName, data); err != nil {
			return err
		}
		*field = html
		return nil
	}

	if err = maybeExecuteSubTemplate("title", &pd.Title); err != nil {
		return err
	}
	if err = maybeExecuteSubTemplate("css", &pd.Css); err != nil {
		return err
	}
	if err = maybeExecuteSubTemplate("js", &pd.Js); err != nil {
		return err
	}

	return nil
}

func setContentTypeUtf8(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
}

func RedirectWithMessage(w http.ResponseWriter, r *http.Request, url, msg string) {
	SetFlash(msg, w)
	http.Redirect(w, r, url, http.StatusSeeOther)
}

func RenderPageOrDie(w http.ResponseWriter, c *Context, name string, data interface{}) {
	fullName := ""
	if c.LoggedIn() {
		fullName = c.Session().FullName
	}

	pd := &PageData{}
	if err := fillPageData(name, data, pd); err != nil {
		ServeError(w, err)
		return
	}
	pd.FullName = fullName
	pd.Message = c.Flash()

	setContentTypeUtf8(w)
	if err := tmpl.ExecuteTemplate(w, "base.html", pd); err != nil {
		ServeError(w, err)
	}
}

func RenderTemplateOrDie(w http.ResponseWriter, name string, data interface{}) {
	setContentTypeUtf8(w)
	if err := tmpl.ExecuteTemplate(w, name, data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// If the returned error is not nil, it is guaranteed to have type
// template.Error.
func ExecuteTemplate(name string, data interface{}) (template.HTML, error) {
	buf := &bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(buf, name, data); err != nil {
		return "", err
	}
	return template.HTML(buf.String()), nil
}

func ExecuteTextTemplate(name string, data interface{}) (string, error) {
	buf := &bytes.Buffer{}
	if err := text_tmpl.ExecuteTemplate(buf, name, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func ServeInfo(w http.ResponseWriter, info string) {
	setContentTypeUtf8(w)
	w.Write([]byte(info))
}

func Serve404(w http.ResponseWriter) {
	http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}

func ServeError(w http.ResponseWriter, data interface{}) {
	http.Error(w, fmt.Sprint(data), http.StatusInternalServerError)
}

type AppHandlerFunc func(http.ResponseWriter, *http.Request, *Context)

// Wraps other http handlers. Creates context object, recovers from panics, etc.
func WrapHandlerImpl(fn AppHandlerFunc, parseForm bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		c := &Context{}

		// See http://blog.golang.org/2010/08/defer-panic-and-recover.html.
		defer func() {
			if data := recover(); data != nil {
				c.Aec().Errorf(fmt.Sprint(data))
				ServeError(w, data)
			}
		}()

		// Initialize the request context object.
		c.SetAec(appengine.NewContext(r))
		CheckError(ReadSession(r, c))
		if msg, err := ConsumeFlash(w, r); err != nil && err != http.ErrNoCookie {
			ServeError(w, err)
			return
		} else {
			c.SetFlash(msg)
		}

		if parseForm {
			CheckError(r.ParseForm())
		}
		fn(w, r, c)
	}
}

func WrapHandler(fn AppHandlerFunc) http.HandlerFunc {
	return WrapHandlerImpl(fn, true)
}

func WrapHandlerNoParseForm(fn AppHandlerFunc) http.HandlerFunc {
	return WrapHandlerImpl(fn, false)
}

type ErrorWithStackTrace struct {
	Stack []byte // from debug.Stack()
	Err   error
}

func (e *ErrorWithStackTrace) Error() string {
	if kPrintStackTrace {
		return fmt.Sprintf("%s\n%v", e.Stack, e.Err)
	}
	return fmt.Sprint(e.Err)
}

func CheckError(err error, v ...interface{}) {
	if err != nil {
		if len(v) > 0 {
			err = errors.New(fmt.Sprintf("%v\n%v", err, v))
		}
		e := &ErrorWithStackTrace{
			Stack: debug.Stack(),
			Err:   err,
		}
		panic(e)
	}
}

func Assert(condition bool, v ...interface{}) {
	if !condition {
		e := &ErrorWithStackTrace{
			Stack: debug.Stack(),
			Err:   errors.New(fmt.Sprint(v...)),
		}
		panic(e)
	}
}

func GenerateSecureRandomString() []byte {
	return securecookie.GenerateRandomKey(32)
}

func SaltAndHash(salt []byte, password string) []byte {
	h := sha1.New()
	io.WriteString(h, string(salt))
	io.WriteString(h, password)
	return h.Sum(nil)
}

func AppHostname(c *Context) string {
	if kAppHostname != "" {
		return kAppHostname
	}
	return appengine.DefaultVersionHostname(c.Aec())
}

func AppHostnameForPayPal(c *Context) string {
	if kAppHostnameForPayPal != "" {
		return kAppHostnameForPayPal
	}
	return AppHostname(c)
}

func ContainsString(slice []string, str string) bool {
	for _, v := range slice {
		if str == v {
			return true
		}
	}
	return false
}
<|endoftext|>