<commit_before>package process

import (
	"fmt"
	"os"
	"path/filepath"

	"golang.org/x/net/context"
	"kego.io/context/cmdctx"
	"kego.io/context/envctx"
	"kego.io/kerr"
	"kego.io/process/generate"
	"kego.io/process/scan"
)

type SourceType string

const (
	S_STRUCTS SourceType = "structs"
	S_TYPES              = "types"
)

// Generate generates the source code from templates and writes the files
// to the correct folders.
//
// file == F_STRUCTS: generated-structs.go in the root of the package.
//
// file == F_TYPES: generated-types.go containing advanced type information
// in the "types" sub package. Note that to generate this file, we need to
// have the main generated-structs.go compiled in, so we generate a temporary
// command and run it with "go run".
//
// file == F_EDITOR: generated-editor.go in the "editor" sub-package. This
// will be compiled to JS when the editor is launched.
//
func Generate(ctx context.Context, file SourceType) error {

	cmd, ok := cmdctx.FromContext(ctx)
	if !ok {
		return kerr.New("LQVDAAQKWK", nil, "No cmd in ctx")
	}

	env, ok := envctx.FromContext(ctx)
	if !ok {
		return kerr.New("IREJTPARUK", nil, "No env in ctx")
	}

	if cmd.Verbose {
		fmt.Print("Generating ", file, "... ")
	}

	if file == S_STRUCTS {
		hasFiles, err := scan.HasSourceFiles(ctx)
		if err != nil {
			return kerr.New("GXGGDQVHHP", err, "ScanForKegoFiles")
		}
		if !hasFiles {
			return kerr.New("YSLREDFDLJ", nil, "No kego files found")
		}
	}

	if file == S_STRUCTS || file == S_TYPES {

		// We only tolerate unknown types when we're initially building the
		// struct files. At all other times, the generated structs should
		// provide all types.
		ignoreUnknownTypes := file == S_STRUCTS

		// When generating structs or types, we need to scan for types. All other runs will have
		// them compiled in the types sub-package.
		if err := scan.ScanForTypes(ctx, ignoreUnknownTypes); err != nil {
			return kerr.New("XYIUHERDHE", err, "scan.ScanForTypes")
		}
	}

	var outputDir string
	var filename string
	var source []byte
	var err error

	switch file {
	case S_STRUCTS:
		outputDir = cmd.Dir
		filename = "generated-structs.go"
		source, err = generate.Structs(ctx)
	case S_TYPES:
		outputDir = filepath.Join(cmd.Dir, "types")
		filename = "generated-types.go"
		source, err = generate.Types(ctx)
	}
	if err != nil {
		return kerr.New("XFNESBLBTQ", err, "generate: %s", file)
	}

	// We only backup in the system structs and types files because they are the only
	// generated files we ever need to roll back
	backup := env.Path == "kego.io/system" && (file == S_STRUCTS || file == S_TYPES)

	if err = save(outputDir, source, filename, backup); err != nil {
		return kerr.New("UONJTTSTWW", err, "save")
	} else {
		if cmd.Verbose {
			fmt.Println("OK.")
		}
	}

	return nil
}

func save(dir string, contents []byte, name string, backup bool) error {

	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0777); err != nil {
			return kerr.New("BPGOUIYPXO", err, "os.MkdirAll")
		}
	}

	file := filepath.Join(dir, name)

	if backup {
		backupPath := filepath.Join(dir, fmt.Sprintf("%s.backup", name))
		if _, err := os.Stat(backupPath); err == nil {
			os.Remove(backupPath)
		}
		if _, err := os.Stat(file); err == nil {
			os.Rename(file, backupPath)
		}
	} else {
		os.Remove(file)
	}

	if len(contents) == 0 {
		return nil
	}

	output, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		return kerr.New("NWLWHSGJWP", err, "os.OpenFile (could not open output file)")
	}
	defer output.Close()

	if _, err := output.Write(contents); err != nil {
		return kerr.New("FBMGPRWQBL", err, "output.Write")
	}

	if err := output.Sync(); err != nil {
		return kerr.New("EGFNTMNKFX", err, "output.Sync")
	}

	return nil
}
<commit_msg>Cancel save generated file if contents is empty moved to before backup process<commit_after>package process

import (
	"fmt"
	"os"
	"path/filepath"

	"golang.org/x/net/context"
	"kego.io/context/cmdctx"
	"kego.io/context/envctx"
	"kego.io/kerr"
	"kego.io/process/generate"
	"kego.io/process/scan"
)

type SourceType string

const (
	S_STRUCTS SourceType = "structs"
	S_TYPES              = "types"
)

// Generate generates the source code from templates and writes the files
// to the correct folders.
//
// file == F_STRUCTS: generated-structs.go in the root of the package.
//
// file == F_TYPES: generated-types.go containing advanced type information
// in the "types" sub package. Note that to generate this file, we need to
// have the main generated-structs.go compiled in, so we generate a temporary
// command and run it with "go run".
//
// file == F_EDITOR: generated-editor.go in the "editor" sub-package. This
// will be compiled to JS when the editor is launched.
//
func Generate(ctx context.Context, file SourceType) error {

	cmd, ok := cmdctx.FromContext(ctx)
	if !ok {
		return kerr.New("LQVDAAQKWK", nil, "No cmd in ctx")
	}

	env, ok := envctx.FromContext(ctx)
	if !ok {
		return kerr.New("IREJTPARUK", nil, "No env in ctx")
	}

	if cmd.Verbose {
		fmt.Print("Generating ", file, "... ")
	}

	if file == S_STRUCTS {
		hasFiles, err := scan.HasSourceFiles(ctx)
		if err != nil {
			return kerr.New("GXGGDQVHHP", err, "ScanForKegoFiles")
		}
		if !hasFiles {
			return kerr.New("YSLREDFDLJ", nil, "No kego files found")
		}
	}

	if file == S_STRUCTS || file == S_TYPES {

		// We only tolerate unknown types when we're initially building the
		// struct files. At all other times, the generated structs should
		// provide all types.
		ignoreUnknownTypes := file == S_STRUCTS

		// When generating structs or types, we need to scan for types. All other runs will have
		// them compiled in the types sub-package.
		if err := scan.ScanForTypes(ctx, ignoreUnknownTypes); err != nil {
			return kerr.New("XYIUHERDHE", err, "scan.ScanForTypes")
		}
	}

	var outputDir string
	var filename string
	var source []byte
	var err error

	switch file {
	case S_STRUCTS:
		outputDir = cmd.Dir
		filename = "generated-structs.go"
		source, err = generate.Structs(ctx)
	case S_TYPES:
		outputDir = filepath.Join(cmd.Dir, "types")
		filename = "generated-types.go"
		source, err = generate.Types(ctx)
	}
	if err != nil {
		return kerr.New("XFNESBLBTQ", err, "generate: %s", file)
	}

	// We only backup in the system structs and types files because they are the only
	// generated files we ever need to roll back
	backup := env.Path == "kego.io/system" && (file == S_STRUCTS || file == S_TYPES)

	if err = save(outputDir, source, filename, backup); err != nil {
		return kerr.New("UONJTTSTWW", err, "save")
	} else {
		if cmd.Verbose {
			fmt.Println("OK.")
		}
	}

	return nil
}

func save(dir string, contents []byte, name string, backup bool) error {

	if len(contents) == 0 {
		return nil
	}

	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err = os.MkdirAll(dir, 0777); err != nil {
			return kerr.New("BPGOUIYPXO", err, "os.MkdirAll")
		}
	}

	file := filepath.Join(dir, name)

	if backup {
		backupPath := filepath.Join(dir, fmt.Sprintf("%s.backup", name))
		if _, err := os.Stat(backupPath); err == nil {
			os.Remove(backupPath)
		}
		if _, err := os.Stat(file); err == nil {
			os.Rename(file, backupPath)
		}
	} else {
		os.Remove(file)
	}

	output, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		return kerr.New("NWLWHSGJWP", err, "os.OpenFile (could not open output file)")
	}
	defer output.Close()

	if _, err := output.Write(contents); err != nil {
		return kerr.New("FBMGPRWQBL", err, "output.Write")
	}

	if err := output.Sync(); err != nil {
		return kerr.New("EGFNTMNKFX", err, "output.Sync")
	}

	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/tls"
	"errors"
	"log"
	"net/http"
	"time"

	"github.com/spf13/viper"

	"golang.org/x/crypto/acme/autocert"
)

func Start(handler http.Handler) error {
	useTLS := viper.GetBool("use_tls")

	s := http.Server{
		Handler:      handler,
		ReadTimeout:  30 * time.Second,
		WriteTimeout: 30 * time.Second,
	}

	if useTLS {
		domain := viper.GetString("domain")
		if domain == "" {
			return errors.New("cannot serve with TLS if no domain specified")
		}

		certManager := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(domain, "www."+domain),
			Cache:      autocert.DirCache("certs"),
			Email:      viper.GetString("cert_email"),
		}

		s.Addr = ":8443"
		s.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate}
		log.Print("server: listening on :8443..")

		go func() {
			http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
				http.Redirect(w, req, "https://"+domain+req.RequestURI, http.StatusMovedPermanently)
			}))
		}()

		return s.ListenAndServeTLS("", "")
	}

	s.Addr = ":8000"
	log.Print("server: listening on :8000..")

	return s.ListenAndServe()
}
<commit_msg>Fix HTTP redirect<commit_after>package server

import (
	"crypto/tls"
	"errors"
	"log"
	"net/http"
	"time"

	"github.com/spf13/viper"

	"golang.org/x/crypto/acme/autocert"
)

func Start(handler http.Handler) error {
	useTLS := viper.GetBool("use_tls")

	s := http.Server{
		Handler:      handler,
		ReadTimeout:  30 * time.Second,
		WriteTimeout: 30 * time.Second,
	}

	if useTLS {
		domain := viper.GetString("domain")
		if domain == "" {
			return errors.New("cannot serve with TLS if no domain specified")
		}

		certManager := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(domain, "www."+domain),
			Cache:      autocert.DirCache("certs"),
			Email:      viper.GetString("cert_email"),
		}

		s.Addr = ":8443"
		s.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate}
		log.Print("server: listening on :8443..")

		go func() {
			err := http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
				http.Redirect(w, req, "https://"+domain+req.RequestURI, http.StatusMovedPermanently)
			}))

			if err != nil {
				log.Fatal(err)
			}
		}()

		return s.ListenAndServeTLS("", "")
	}

	s.Addr = ":8000"
	log.Print("server: listening on :8000..")

	return s.ListenAndServe()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"

	"github.com/gorilla/mux"
	. "github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/ipam"
	"github.com/weaveworks/weave/mesh"
	"github.com/weaveworks/weave/nameserver"
	"github.com/weaveworks/weave/net/address"
	weave "github.com/weaveworks/weave/router"
)

var rootTemplate = template.New("root").Funcs(map[string]interface{}{
	"countDNSEntries": func(entries []nameserver.EntryStatus) int {
		count := 0
		for _, entry := range entries {
			if entry.Tombstone == 0 {
				count++
			}
		}
		return count
	},
	"upstreamServers": func(servers []string) string {
		if len(servers) == 0 {
			return "none"
		}
		return strings.Join(servers, ", ")
	},
	"printIPAMRanges": func(status ipam.Status) string {
		var buffer bytes.Buffer

		type NicknameStats struct {
			Ranges    []address.Range
			Addresses []address.Address
			Reachable bool
		}

		nicknameStats := make(map[string]*NicknameStats)
		getNickName := func(peer mesh.PeerName) string {
			name, found := status.Nicknames[peer]
			if !found {
				return fmt.Sprintf("%s", peer)
			}
			return name
		}
		getOrCreateNicknameStats := func(peer mesh.PeerName) *NicknameStats {
			peerName := getNickName(peer)
			stats, found := nicknameStats[peerName]
			if !found {
				stats = &NicknameStats{
					Ranges:    []address.Range{},
					Addresses: []address.Address{},
					Reachable: status.IsKnownPeer(peer),
				}
				nicknameStats[peerName] = stats
			}
			return stats
		}

		for _, entry := range status.Ring.Entries {
			stats := getOrCreateNicknameStats(entry.Peer)
			stats.Addresses = append(stats.Addresses, entry.Token)
		}

		for peer, ranges := range status.Ring.OwnedRangesByPeer() {
			stats := getOrCreateNicknameStats(peer)
			stats.Ranges = append(stats.Ranges, ranges...)
		}

		appendAddresses := func(displayName string, reachable bool, addresses []address.Address, ranges []address.Range) {
			reachableStr := ""
			if !reachable {
				reachableStr = "- unreachable!"
			}
			ipsInRange := 0
			for _, chunk := range ranges {
				ipsInRange += int(chunk.Size())
			}
			percentageRanges := float32(ipsInRange) * 100.0 / float32(status.RangeNumIPs)

			fmt.Fprintf(&buffer, "%20s: %8d IPs (%04.1f%% of universe, in %3d ranges) - used: %8d IPs %s\n",
				displayName, ipsInRange, percentageRanges, len(ranges), len(addresses), reachableStr)
		}

		// print the local addresses
		ourNickname := getNickName(status.OurName)
		ourStats := nicknameStats[ourNickname]
		appendAddresses("(local)", true, ourStats.Addresses, ourStats.Ranges)

		// and then the rest
		for nickname, stats := range nicknameStats {
			if nickname != ourNickname {
				appendAddresses(nickname, stats.Reachable, stats.Addresses, stats.Ranges)
			}
		}

		return buffer.String()
	},
	"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
		counts := make(map[string]int)
		for _, conn := range conns {
			counts[conn.State]++
		}
		return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
	},
	"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
		counts := make(map[string]int)
		for _, peer := range peers {
			for _, conn := range peer.Connections {
				if conn.Established {
					counts["established"]++
				} else {
					counts["pending"]++
				}
			}
		}
		return printCounts(counts, []string{"established", "pending"})
	},
	"printState": func(enabled bool) string {
		if enabled {
			return "enabled"
		}
		return "disabled"
	},
	"trimSuffix": strings.TrimSuffix,
})

// Print counts in a specified order
func printCounts(counts map[string]int, keys []string) string {
	var stringCounts []string
	for _, key := range keys {
		if count, ok := counts[key]; ok {
			stringCounts = append(stringCounts, fmt.Sprintf("%d %s", count, key))
		}
	}
	return strings.Join(stringCounts, ", ")
}

// Strip escaped newlines from template
func escape(template string) string {
	return strings.Replace(template, "\\\n", "", -1)
}

// Define a named template panicking on error
func defTemplate(name string, text string) *template.Template {
	return template.Must(rootTemplate.New(name).Parse(escape(text)))
}

var statusTemplate = defTemplate("status", `\
       Version: {{.Version}}

       Service: router
      Protocol: {{.Router.Protocol}} \
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
{{.Router.ProtocolMaxVersion}}\
{{else}}\
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
{{end}}
          Name: {{.Router.Name}}({{.Router.NickName}})
    Encryption: {{printState .Router.Encryption}}
 PeerDiscovery: {{printState .Router.PeerDiscovery}}
       Targets: {{len .Router.Targets}}
   Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
         Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
{{if .IPAM}}\

       Service: ipam
{{if .IPAM.Entries}}\
     Consensus: achieved
{{else if .IPAM.Paxos}}\
     Consensus: waiting (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{else}}\
     Consensus: deferred
{{end}}\
         Range: {{.IPAM.Range}}
 DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{end}}\
{{if .DNS}}\

       Service: dns
        Domain: {{.DNS.Domain}}
      Upstream: {{upstreamServers .DNS.Upstream}}
           TTL: {{.DNS.TTL}}
       Entries: {{countDNSEntries .DNS.Entries}}
{{end}}\
`)

var targetsTemplate = defTemplate("targetsTemplate", `\
{{range .Router.Targets}}{{.}}
{{end}}\
`)

var connectionsTemplate = defTemplate("connectionsTemplate", `\
{{range .Router.Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} {{printf "%-11v" .State}} {{.Info}}
{{end}}\
`)

var peersTemplate = defTemplate("peers", `\
{{range .Router.Peers}}\
{{.Name}}({{.NickName}})
{{range .Connections}}\
   {{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-32v" $nameNickName}} \
{{if .Established}}established{{else}}pending{{end}}
{{end}}\
{{end}}\
`)

var dnsEntriesTemplate = defTemplate("dnsEntries", `\
{{$domain := printf ".%v" .DNS.Domain}}\
{{range .DNS.Entries}}\
{{if eq .Tombstone 0}}\
{{$hostname := trimSuffix .Hostname $domain}}\
{{printf "%-12v" $hostname}} {{printf "%-15v" .Address}} {{printf "%12.12v" .ContainerID}} {{.Origin}}
{{end}}\
{{end}}\
`)

var ipamTemplate = defTemplate("ipamTemplate", `\
      Universe: {{.IPAM.Range}} ({{.IPAM.RangeNumIPs}} IPs)
 DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{if .IPAM.Owned}}\
    Containers:
{{range $id, $addresses := .IPAM.Owned}}\
                {{printf "%-12s" $id}}: {{$addresses}}
{{end}}\
{{end}}\
    Ownerships:
{{printIPAMRanges .IPAM}}
{{if .IPAM.PendingClaims}}\
        Claims:
{{range .IPAM.PendingClaims}}
                {{printf "%-15v" .Ident}} {{.Address}}
{{end}}\
{{end}}\
`)

type WeaveStatus struct {
	Version string
	Router  *weave.NetworkRouterStatus `json:"Router,omitempty"`
	IPAM    *ipam.Status               `json:"IPAM,omitempty"`
	DNS     *nameserver.Status         `json:"DNS,omitempty"`
}

func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
	status := func() WeaveStatus {
		return WeaveStatus{
			version,
			weave.NewNetworkRouterStatus(router),
			ipam.NewStatus(allocator, defaultSubnet),
			nameserver.NewStatus(ns, dnsserver)}
	}
	muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			json, err := json.MarshalIndent(status(), "", "    ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				Log.Error("Error during report marshalling: ", err)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(json)
		})

	muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			formatTemplate, err := template.New("format").Parse(mux.Vars(r)["format"])
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			if err := formatTemplate.Execute(w, status()); err != nil {
				http.Error(w, "error during template execution", http.StatusInternalServerError)
				Log.Error(err)
			}
		})

	defHandler := func(path string, template *template.Template) {
		muxRouter.Methods("GET").Path(path).HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				if err := template.Execute(w, status()); err != nil {
					http.Error(w, "error during template execution", http.StatusInternalServerError)
					Log.Error(err)
				}
			})
	}

	defHandler("/status", statusTemplate)
	defHandler("/status/targets", targetsTemplate)
	defHandler("/status/connections", connectionsTemplate)
	defHandler("/status/peers", peersTemplate)
	defHandler("/status/dns", dnsEntriesTemplate)
	defHandler("/status/ipam", ipamTemplate)

}
<commit_msg>Simplify ipam status format Don't use the word "universe" in user-visible output; it is an internal term. Removed everything but owned ranges from 'weave status ipam' results, for consistency with similar functions<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"

	"github.com/gorilla/mux"
	. "github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/ipam"
	"github.com/weaveworks/weave/mesh"
	"github.com/weaveworks/weave/nameserver"
	"github.com/weaveworks/weave/net/address"
	weave "github.com/weaveworks/weave/router"
)

var rootTemplate = template.New("root").Funcs(map[string]interface{}{
	"countDNSEntries": func(entries []nameserver.EntryStatus) int {
		count := 0
		for _, entry := range entries {
			if entry.Tombstone == 0 {
				count++
			}
		}
		return count
	},
	"upstreamServers": func(servers []string) string {
		if len(servers) == 0 {
			return "none"
		}
		return strings.Join(servers, ", ")
	},
	"printIPAMRanges": func(status ipam.Status) string {
		var buffer bytes.Buffer

		type NicknameStats struct {
			Ranges    []address.Range
			Addresses []address.Address
			Reachable bool
		}

		nicknameStats := make(map[string]*NicknameStats)
		getNickName := func(peer mesh.PeerName) string {
			name, found := status.Nicknames[peer]
			if !found {
				return peer.String()
			}
			return name
		}
		getOrCreateNicknameStats := func(peer mesh.PeerName) *NicknameStats {
			peerName := getNickName(peer)
			stats, found := nicknameStats[peerName]
			if !found {
				stats = &NicknameStats{Reachable: status.IsKnownPeer(peer)}
				nicknameStats[peerName] = stats
			}
			return stats
		}

		for _, entry := range status.Ring.Entries {
			stats := getOrCreateNicknameStats(entry.Peer)
			stats.Addresses = append(stats.Addresses, entry.Token)
		}

		for peer, ranges := range status.Ring.OwnedRangesByPeer() {
			stats := getOrCreateNicknameStats(peer)
			stats.Ranges = append(stats.Ranges, ranges...)
		}

		appendAddresses := func(displayName string, reachable bool, addresses []address.Address, ranges []address.Range) {
			reachableStr := ""
			if !reachable {
				reachableStr = "- unreachable!"
			}
			ipsInRange := 0
			for _, chunk := range ranges {
				ipsInRange += int(chunk.Size())
			}
			percentageRanges := float32(ipsInRange) * 100.0 / float32(status.RangeNumIPs)

			fmt.Fprintf(&buffer, "%20s: %8d IPs (%04.1f%% of total) - used: %d %s\n",
				displayName, ipsInRange, percentageRanges, len(addresses), reachableStr)
		}

		// print the local addresses
		ourNickname := getNickName(status.OurName)
		ourStats := nicknameStats[ourNickname]
		appendAddresses("(local)", true, ourStats.Addresses, ourStats.Ranges)

		// and then the rest
		for nickname, stats := range nicknameStats {
			if nickname != ourNickname {
				appendAddresses(nickname, stats.Reachable, stats.Addresses, stats.Ranges)
			}
		}

		return buffer.String()
	},
	"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
		counts := make(map[string]int)
		for _, conn := range conns {
			counts[conn.State]++
		}
		return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
	},
	"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
		counts := make(map[string]int)
		for _, peer := range peers {
			for _, conn := range peer.Connections {
				if conn.Established {
					counts["established"]++
				} else {
					counts["pending"]++
				}
			}
		}
		return printCounts(counts, []string{"established", "pending"})
	},
	"printState": func(enabled bool) string {
		if enabled {
			return "enabled"
		}
		return "disabled"
	},
	"trimSuffix": strings.TrimSuffix,
})

// Print counts in a specified order
func printCounts(counts map[string]int, keys []string) string {
	var stringCounts []string
	for _, key := range keys {
		if count, ok := counts[key]; ok {
			stringCounts = append(stringCounts, fmt.Sprintf("%d %s", count, key))
		}
	}
	return strings.Join(stringCounts, ", ")
}

// Strip escaped newlines from template
func escape(template string) string {
	return strings.Replace(template, "\\\n", "", -1)
}

// Define a named template panicking on error
func defTemplate(name string, text string) *template.Template {
	return template.Must(rootTemplate.New(name).Parse(escape(text)))
}

var statusTemplate = defTemplate("status", `\
       Version: {{.Version}}

       Service: router
      Protocol: {{.Router.Protocol}} \
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
{{.Router.ProtocolMaxVersion}}\
{{else}}\
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
{{end}}
          Name: {{.Router.Name}}({{.Router.NickName}})
    Encryption: {{printState .Router.Encryption}}
 PeerDiscovery: {{printState .Router.PeerDiscovery}}
       Targets: {{len .Router.Targets}}
   Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
         Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
{{if .IPAM}}\

       Service: ipam
{{if .IPAM.Entries}}\
     Consensus: achieved
{{else if .IPAM.Paxos}}\
     Consensus: waiting (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{else}}\
     Consensus: deferred
{{end}}\
         Range: {{.IPAM.Range}}
 DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{end}}\
{{if .DNS}}\

       Service: dns
        Domain: {{.DNS.Domain}}
      Upstream: {{upstreamServers .DNS.Upstream}}
           TTL: {{.DNS.TTL}}
       Entries: {{countDNSEntries .DNS.Entries}}
{{end}}\
`)

var targetsTemplate = defTemplate("targetsTemplate", `\
{{range .Router.Targets}}{{.}}
{{end}}\
`)

var connectionsTemplate = defTemplate("connectionsTemplate", `\
{{range .Router.Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} {{printf "%-11v" .State}} {{.Info}}
{{end}}\
`)

var peersTemplate = defTemplate("peers", `\
{{range .Router.Peers}}\
{{.Name}}({{.NickName}})
{{range .Connections}}\
   {{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-32v" $nameNickName}} \
{{if .Established}}established{{else}}pending{{end}}
{{end}}\
{{end}}\
`)

var dnsEntriesTemplate = defTemplate("dnsEntries", `\
{{$domain := printf ".%v" .DNS.Domain}}\
{{range .DNS.Entries}}\
{{if eq .Tombstone 0}}\
{{$hostname := trimSuffix .Hostname $domain}}\
{{printf "%-12v" $hostname}} {{printf "%-15v" .Address}} {{printf "%12.12v" .ContainerID}} {{.Origin}}
{{end}}\
{{end}}\
`)

var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .IPAM}}`)

type WeaveStatus struct {
	Version string
	Router  *weave.NetworkRouterStatus `json:"Router,omitempty"`
	IPAM    *ipam.Status               `json:"IPAM,omitempty"`
	DNS     *nameserver.Status         `json:"DNS,omitempty"`
}

func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
	status := func() WeaveStatus {
		return WeaveStatus{
			version,
			weave.NewNetworkRouterStatus(router),
			ipam.NewStatus(allocator, defaultSubnet),
			nameserver.NewStatus(ns, dnsserver)}
	}
	muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			json, err := json.MarshalIndent(status(), "", "    ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				Log.Error("Error during report marshalling: ", err)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(json)
		})

	muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			formatTemplate, err := template.New("format").Parse(mux.Vars(r)["format"])
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			if err := formatTemplate.Execute(w, status()); err != nil {
				http.Error(w, "error during template execution", http.StatusInternalServerError)
				Log.Error(err)
			}
		})

	defHandler := func(path string, template *template.Template) {
		muxRouter.Methods("GET").Path(path).HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				if err := template.Execute(w, status()); err != nil {
					http.Error(w, "error during template execution", http.StatusInternalServerError)
					Log.Error(err)
				}
			})
	}

	defHandler("/status", statusTemplate)
	defHandler("/status/targets", targetsTemplate)
	defHandler("/status/connections", connectionsTemplate)
	defHandler("/status/peers", peersTemplate)
	defHandler("/status/dns", dnsEntriesTemplate)
	defHandler("/status/ipam", ipamTemplate)

}
<|endoftext|>


<commit_before>package directconnectresourcemanager

import (
	"crypto/tls"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/emperorcow/protectedmap"
	"github.com/jmmcatee/cracklord/common/queue"
)

type resourceInfo struct {
	Notes         string
	ReconnectTime int
}

type directResourceManager struct {
	resources protectedmap.ProtectedMap
	q         *queue.Queue
}

func Setup(qpointer *queue.Queue) queue.ResourceManager {
	return &directResourceManager{
		resources: protectedmap.New(),
		q:         qpointer,
	}
}

func (this directResourceManager) SystemName() string {
	return "directconnect"
}

func (this directResourceManager) DisplayName() string {
	return "Direct Connect"
}

func (this directResourceManager) Description() string {
	return "Directly connect to resource servers."
}

func (this directResourceManager) Parameters() string {
	return `"form": [
	    {
	        "type": "section",
	        "htmlClass": "row",
	        "items": [
	            {
	                "type": "section",
	                "htmlClass": "col-xs-6",
	                "items": [
	                    "reconnect"
	                ]
	            },
	            {
	                "type": "section",
	                "htmlClass": "col-xs-6",
	                "items": [
	                    {
	                        "type": "conditional",
	                        "condition": "modelData.reconnect",
	                        "items": [
	                            "reconnecttime"
	                        ]
	                    }
	                ]
	            }
	        ]
	    },
	    {
	        "key": "notes",
	        "type": "textarea",
	        "placeholder": "OPTIONAL: Any notes you would like to include (location, primary contact, etc.)"
	    }
	],
	"schema": {
		"type": "object",
		"title": "Direct Connect",
		"properties": {
		    "notes": {
			    "title": "Notes",
			    "type": "string"
		    },
		    "reconnect": {
	            "title": "Attempt automatic reconnect?",
	            "type": "boolean",
	            "default": true
	        },
	        "reconnecttime": {
	            "title": "Reconnect Time",
	            "description": "In seconds",
	            "type": "integer",
	            "default": 10
	        }
		}
	}`
}

func (this *directResourceManager) AddResource(name string, address string, params map[string]string, tls *tls.Config) error {
	//First, we attempt to add the resource into the queue itself
	uuid, err := this.q.AddResource(address, name, tls)

	//If unable to connect, log it and return the error to the API
	if err != nil {
		log.WithField("error", err.Error()).Error("Unable to add resource through direct connect manager")
		return err
	}

	//Let's create a temporary resource to hold the info
	tempresource = resourceInfo{
		ReconnectTime: -1,
		Notes:         params[notes],
	}

	//If we were going to try and reconnect (from the boolean parameter), then set the time to the value.
	if params[reconnect] == true {
		tempresource.ReconnectTime = params[reconnecttime]
	}

	//Finally, set the resource into our map
	this.resources.Set(uuid, tempresource)

	return nil
}

func (this *directResourceManager) DeleteResource(resourceid string) error {
	//First, try and delete the resource from the queue itself
	err := this.q.RemoveResource(resourceid)

	//If there was an error, log it back to the API
	if err != nil {
		log.WithField("error", err.Error()).Error("Unable to remove resource through direct connect manager")
		return err
	}

	//Finally, delete the local data from here
	this.resources.Delete(resourceid)
	return nil
}

func (this directResourceManager) GetResource(resourceid string) (queue.Resource, map[string]string, error) {
	return queue.Resource{}, nil
}

func (this *directResourceManager) PauseResource(resourceid string) error {
	return nil
}

func (this *directResourceManager) ResumeResource(resourceid string) error {
	return nil
}

func (this directResourceManager) GetManagedResources() []string {
	return []string{"one", "two"}
}

func (this *directResourceManager) Keep() {
	return
}
<commit_msg>Finished direct connect resource manager plugin<commit_after>package directconnectresourcemanager

import (
	"crypto/tls"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/emperorcow/protectedmap"
	"github.com/jmmcatee/cracklord/common/queue"
	"strconv"
)

type resourceInfo struct {
	Notes     string
	Reconnect bool
}

type directResourceManager struct {
	resources protectedmap.ProtectedMap
	q         *queue.Queue
	tls       *tls.Config
}

func Setup(qpointer *queue.Queue, tlspointer *tls.Config) queue.ResourceManager {
	return &directResourceManager{
		resources: protectedmap.New(),
		q:         qpointer,
		tls:       tlspointer,
	}
}

func (this directResourceManager) SystemName() string {
	return "directconnect"
}

func (this directResourceManager) DisplayName() string {
	return "Direct Connect"
}

func (this directResourceManager) Description() string {
	return "Directly connect to resource servers."
}

func (this directResourceManager) Parameters() string {
	return `"form": [
        "reconnect",
	    {
	        "key": "notes",
	        "type": "textarea",
	        "placeholder": "OPTIONAL: Any notes you would like to include (location, primary contact, etc.)"
	    }
	],
	"schema": {
		"type": "object",
		"title": "Direct Connect",
		"properties": {
		    "notes": {
			    "title": "Notes",
			    "type": "string"
		    },
		    "reconnect": {
	            "title": "Attempt automatic reconnect?",
	            "type": "boolean",
	            "default": true
	        }
		}
	}`
}

func (this *directResourceManager) AddResource(name string, address string, params map[string]string) error {
	//First, we attempt to add the resource into the queue itself
	uuid, err := this.q.AddResource(address, name, this.tls)

	//If unable to connect, log it and return the error to the API
	if err != nil {
		log.WithField("error", err.Error()).Error("Unable to add resource through direct connect manager")
		return err
	}

	//Finally, set the resource into our map
	this.resources.Set(uuid, this.parseParams(params))

	return nil
}

func (this *directResourceManager) DeleteResource(resourceid string) error {
	//First, try and delete the resource from the queue itself
	err := this.q.RemoveResource(resourceid)

	//If there was an error, log it back to the API
	if err != nil {
		log.WithField("error", err.Error()).Error("Unable to remove resource through direct connect manager")
		return err
	}

	//Finally, delete the local data from here
	this.resources.Delete(resourceid)
	return nil
}

func (this directResourceManager) GetResource(resourceid string) (*queue.Resource, map[string]string, error) {
	//First, get the resource itself from the queue
	resource, ok := this.q.GetResource(resourceid)

	//If we weren't able to gather it, return an error
	if !ok {
		return &queue.Resource{}, nil, errors.New("Resource with requested ID not found in the queue.")
	}

	//Now we'll gather the data from our local map of parameters
	localresource, ok := this.resources.Get(resourceid)
	if !ok {
		return &queue.Resource{}, nil, errors.New("Resource wiht requested ID could not be found in direct connect resource manager.")
	}

	//Since our map uses a generic interface{}, we have to cast our result back
	localres := localresource.(resourceInfo)

	//Parse our parameters struct back into a common string map
	parameters := make(map[string]string)
	parameters["notes"] = localres.Notes
	parameters["reconnect"] = strconv.FormatBool(localres.Reconnect)

	return resource, parameters, nil
}

func (this *directResourceManager) UpdateResource(resourceid string, newstatus string, newparams map[string]string) error {
	//Because we need to make some comparisons for pause/resume, let's get the current resource state
	oldresource, _, err := this.GetResource(resourceid)
	if err != nil {
		return err
	}

	//Set the internal parameters within the direct connect manager to the new data
	this.resources.Set(resourceid, this.parseParams(newparams))

	//Check to see if the old status matches the new one, if not, we need to make a change
	if oldresource.Status != newstatus {
		switch newstatus {
		case "resume": //If our new status is resume, then resume the resource
			err = this.q.ResumeResource(resourceid)
			if err != nil {
				return err
			}
			break

		case "pause": //If the new status is pause, pause the resource in the queue
			err = this.q.PauseResource(resourceid)
			if err != nil {
				return err
			}
			break
		}
	}

	//Finally, we can return a nil as we were successful
	return nil
}

func (this directResourceManager) GetManagedResources() []string {
	//We need to make a slice of resource UUID strings for every resource we manage.  First, let's make the actual slice with a length of the size of our map
	resourceids := make([]string, this.resources.Count())

	//Next let's start up an iterator for our map and loop through each resource
	iter := this.resources.Iterator()
	for data := range iter.Loop() {
		//Now let's add the ID from the map to the slice of UUIDs
		resourceids = append(resourceids, data.Key)
	}

	return resourceids
}

//This function loops through all of the directly connected resources and detects
//that resource is still connected.  If so, it will do nothing; however, if not
//then it will attempt to reconnect if at all possible.
func (this *directResourceManager) Keep() {
	iter := this.resources.Iterator()
	for data := range iter.Loop() {
		localResource := data.Val.(resourceInfo)
		queueResource, ok := this.q.GetResource(data.Key)
		if !ok {
			log.WithField("resourceid", data.Key).Error("Unable to find a resource in the queue that the direct connect manager thought it was responsible for.")
			continue
		}
		status := this.q.CheckResourceConnectionStatus(queueResource)
		if !status && localResource.Reconnect {
			log.WithField("resource", queueResource.Name).Info("Attempting to reconnect to directly connected resource.")
			this.q.ConnectResource(queueResource, this.tls)
		}
	}

	log.Info("Direct connect resource manager has successfully updated resources.")
}

func (this *directResourceManager) parseParams(params map[string]string) resourceInfo {
	//Let's create a temporary resource to hold the info
	tempresource := resourceInfo{
		Reconnect: false,
		Notes:     params["notes"],
	}

	//Set the boolean parameter for reconnect if true.
	if params["reconnect"] == "true" {
		tempresource.Reconnect = true
	}

	return tempresource
}
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

 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 programs

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"github.com/pufferpanel/apufferi/common"
	"github.com/pufferpanel/apufferi/logging"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/programs/operations"
)

type Program interface {
	//Starts the program.
	//This includes starting the environment if it is not running.
	Start() (err error)

	//Stops the program.
	//This will also stop the environment it is ran in.
	Stop() (err error)

	//Kills the program.
	//This will also stop the environment it is ran in.
	Kill() (err error)

	//Creates any files needed for the program.
	//This includes creating the environment.
	Create() (err error)

	//Destroys the server.
	//This will delete the server, environment, and any files related to it.
	Destroy() (err error)

	Install() (err error)

	//Determines if the server is running.
	IsRunning() (isRunning bool)

	//Sends a command to the process
	//If the program supports input, this will send the arguments to that.
	Execute(command string) (err error)

	SetEnabled(isEnabled bool) (err error)

	IsEnabled() (isEnabled bool)

	SetAutoStart(isAutoStart bool) (err error)

	IsAutoStart() (isAutoStart bool)

	SetEnvironment(environment environments.Environment) (err error)

	Id() string

	GetEnvironment() environments.Environment

	Save(file string) (err error)

	Edit(data map[string]interface{}) (err error)

	GetData() map[string]DataObject

	GetNetwork() string
}

//Starts the program.
//This includes starting the environment if it is not running.
func (p *ProgramData) Start() (err error) {
	logging.Debugf("Starting server %s", p.Id())
	p.Environment.DisplayToConsole("Starting server\n")
	data := make(map[string]interface{})
	for k, v := range p.Data {
		data[k] = v.Value
	}

	err = p.Environment.ExecuteAsync(p.RunData.Program, common.ReplaceTokensInArr(p.RunData.Arguments, data), func(graceful bool) {
		if (graceful && p.RunData.AutoRestartFromGraceful) || (!graceful && p.RunData.AutoRestartFromCrash) {
			p.Start()
		}
	})
	if err != nil {
		logging.Error("Error starting server", err)
		p.Environment.DisplayToConsole("Failed to start server\n")
	}

	return
}

//Stops the program.
//This will also stop the environment it is ran in.
func (p *ProgramData) Stop() (err error) {
	logging.Debugf("Stopping server %s", p.Id())
	err = p.Environment.ExecuteInMainProcess(p.RunData.Stop)
	if err != nil {
		p.Environment.DisplayToConsole("Failed to stop server\n")
	} else {
		p.Environment.DisplayToConsole("Server stopped\n")
	}
	return
}

//Kills the program.
//This will also stop the environment it is ran in.
func (p *ProgramData) Kill() (err error) {
	logging.Debugf("Killing server %s", p.Id())
	err = p.Environment.Kill()
	if err != nil {
		p.Environment.DisplayToConsole("Failed to kill server\n")
	} else {
		p.Environment.DisplayToConsole("Server killed\n")
	}
	return
}

//Creates any files needed for the program.
//This includes creating the environment.
func (p *ProgramData) Create() (err error) {
	logging.Debugf("Creating server %s", p.Id())
	p.Environment.DisplayToConsole("Allocating server\n")
	err = p.Environment.Create()
	p.Environment.DisplayToConsole("Server allocated\n")
	p.Environment.DisplayToConsole("Ready to be installed\n")
	return
}

//Destroys the server.
//This will delete the server, environment, and any files related to it.
func (p *ProgramData) Destroy() (err error) {
	logging.Debugf("Destroying server %s", p.Id())
	err = p.Environment.Delete()
	return
}

func (p *ProgramData) Install() (err error) {
	logging.Debugf("Installing server %s", p.Id())
	if p.IsRunning() {
		err = p.Stop()
	}

	if err != nil {
		logging.Error("Error stopping server to install: ", err)
		p.Environment.DisplayToConsole("Error stopping server\n")
		return
	}

	p.Environment.DisplayToConsole("Installing server\n")

	os.MkdirAll(p.Environment.GetRootDirectory(), 0755)

	process := operations.GenerateProcess(p.InstallData.Operations, p.Environment, p.DataToMap())
	err = process.Run()
	if err != nil {
		p.Environment.DisplayToConsole("Error running installer, check daemon logs")
	} else {
		p.Environment.DisplayToConsole("Server installed\n")
	}
	return
}

//Determines if the server is running.
func (p *ProgramData) IsRunning() (isRunning bool) {
	isRunning = p.Environment.IsRunning()
	return
}

//Sends a command to the process
//If the program supports input, this will send the arguments to that.
func (p *ProgramData) Execute(command string) (err error) {
	err = p.Environment.ExecuteInMainProcess(command)
	return
}

func (p *ProgramData) SetEnabled(isEnabled bool) (err error) {
	p.RunData.Enabled = isEnabled
	return
}

func (p *ProgramData) IsEnabled() (isEnabled bool) {
	isEnabled = p.RunData.Enabled
	return
}

func (p *ProgramData) SetEnvironment(environment environments.Environment) (err error) {
	p.Environment = environment
	return
}

func (p *ProgramData) Id() string {
	return p.Identifier
}

func (p *ProgramData) GetEnvironment() environments.Environment {
	return p.Environment
}

func (p *ProgramData) SetAutoStart(isAutoStart bool) (err error) {
	p.RunData.AutoStart = isAutoStart
	return
}

func (p *ProgramData) IsAutoStart() (isAutoStart bool) {
	isAutoStart = p.RunData.AutoStart
	return
}

func (p *ProgramData) Save(file string) (err error) {
	logging.Debugf("Saving server %s", p.Id())

	endResult := make(map[string]interface{})
	endResult["pufferd"] = p

	data, err := json.MarshalIndent(endResult, "", "  ")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(file, data, 0664)
	return
}

func (p *ProgramData) Edit(data map[string]interface{}) (err error) {
	for k, v := range data {
		if v == nil || v == "" {
			delete(p.Data, k)
		}

		var elem DataObject

		if _, ok := p.Data[k]; ok {
			elem = p.Data[k]
		} else {
			elem = DataObject{}
		}
		elem.Value = v

		p.Data[k] = elem
	}
	err = Save(p.Id())
	return
}

func (p *ProgramData) GetData() map[string]DataObject {
	return p.Data
}

func (p *ProgramData) GetNetwork() string {
	data := p.GetData()
	ip := "0.0.0.0"
	port := "0"

	if ipData, ok := data["ip"]; ok {
		ip = ipData.Value.(string)
	}

	if portData, ok := data["port"]; ok {
		port = portData.Value.(string)
	}

	return ip + ":" + port
}<commit_msg>Enforce enable so it does not allow installation or starting servers<commit_after>/*
 Copyright 2016 Padduck, LLC

 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 programs

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"github.com/pufferpanel/apufferi/common"
	"github.com/pufferpanel/apufferi/logging"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/programs/operations"
	"errors"
)

type Program interface {
	//Starts the program.
	//This includes starting the environment if it is not running.
	Start() (err error)

	//Stops the program.
	//This will also stop the environment it is ran in.
	Stop() (err error)

	//Kills the program.
	//This will also stop the environment it is ran in.
	Kill() (err error)

	//Creates any files needed for the program.
	//This includes creating the environment.
	Create() (err error)

	//Destroys the server.
	//This will delete the server, environment, and any files related to it.
	Destroy() (err error)

	Install() (err error)

	//Determines if the server is running.
	IsRunning() (isRunning bool)

	//Sends a command to the process
	//If the program supports input, this will send the arguments to that.
	Execute(command string) (err error)

	SetEnabled(isEnabled bool) (err error)

	IsEnabled() (isEnabled bool)

	SetAutoStart(isAutoStart bool) (err error)

	IsAutoStart() (isAutoStart bool)

	SetEnvironment(environment environments.Environment) (err error)

	Id() string

	GetEnvironment() environments.Environment

	Save(file string) (err error)

	Edit(data map[string]interface{}) (err error)

	GetData() map[string]DataObject

	GetNetwork() string
}

//Starts the program.
//This includes starting the environment if it is not running.
func (p *ProgramData) Start() (err error) {
	if !p.IsEnabled() {
		logging.Errorf("Server %s is not enabled, cannot start", p.Id())
		return errors.New("server not enabled")
	}
	logging.Debugf("Starting server %s", p.Id())
	p.Environment.DisplayToConsole("Starting server\n")
	data := make(map[string]interface{})
	for k, v := range p.Data {
		data[k] = v.Value
	}

	err = p.Environment.ExecuteAsync(p.RunData.Program, common.ReplaceTokensInArr(p.RunData.Arguments, data), func(graceful bool) {
		if (graceful && p.RunData.AutoRestartFromGraceful) || (!graceful && p.RunData.AutoRestartFromCrash) {
			p.Start()
		}
	})
	if err != nil {
		logging.Error("Error starting server", err)
		p.Environment.DisplayToConsole("Failed to start server\n")
	}

	return
}

//Stops the program.
//This will also stop the environment it is ran in.
func (p *ProgramData) Stop() (err error) {
	logging.Debugf("Stopping server %s", p.Id())
	err = p.Environment.ExecuteInMainProcess(p.RunData.Stop)
	if err != nil {
		p.Environment.DisplayToConsole("Failed to stop server\n")
	} else {
		p.Environment.DisplayToConsole("Server stopped\n")
	}
	return
}

//Kills the program.
//This will also stop the environment it is ran in.
func (p *ProgramData) Kill() (err error) {
	logging.Debugf("Killing server %s", p.Id())
	err = p.Environment.Kill()
	if err != nil {
		p.Environment.DisplayToConsole("Failed to kill server\n")
	} else {
		p.Environment.DisplayToConsole("Server killed\n")
	}
	return
}

//Creates any files needed for the program.
//This includes creating the environment.
func (p *ProgramData) Create() (err error) {
	logging.Debugf("Creating server %s", p.Id())
	p.Environment.DisplayToConsole("Allocating server\n")
	err = p.Environment.Create()
	p.Environment.DisplayToConsole("Server allocated\n")
	p.Environment.DisplayToConsole("Ready to be installed\n")
	return
}

//Destroys the server.
//This will delete the server, environment, and any files related to it.
func (p *ProgramData) Destroy() (err error) {
	logging.Debugf("Destroying server %s", p.Id())
	err = p.Environment.Delete()
	return
}

func (p *ProgramData) Install() (err error) {
	if !p.IsEnabled() {
		logging.Errorf("Server %s is not enabled, cannot install", p.Id())
		return errors.New("server not enabled")
	}

	logging.Debugf("Installing server %s", p.Id())
	if p.IsRunning() {
		err = p.Stop()
	}

	if err != nil {
		logging.Error("Error stopping server to install: ", err)
		p.Environment.DisplayToConsole("Error stopping server\n")
		return
	}

	p.Environment.DisplayToConsole("Installing server\n")

	os.MkdirAll(p.Environment.GetRootDirectory(), 0755)

	process := operations.GenerateProcess(p.InstallData.Operations, p.Environment, p.DataToMap())
	err = process.Run()
	if err != nil {
		p.Environment.DisplayToConsole("Error running installer, check daemon logs")
	} else {
		p.Environment.DisplayToConsole("Server installed\n")
	}
	return
}

//Determines if the server is running.
func (p *ProgramData) IsRunning() (isRunning bool) {
	isRunning = p.Environment.IsRunning()
	return
}

//Sends a command to the process
//If the program supports input, this will send the arguments to that.
func (p *ProgramData) Execute(command string) (err error) {
	err = p.Environment.ExecuteInMainProcess(command)
	return
}

func (p *ProgramData) SetEnabled(isEnabled bool) (err error) {
	p.RunData.Enabled = isEnabled
	return
}

func (p *ProgramData) IsEnabled() (isEnabled bool) {
	isEnabled = p.RunData.Enabled
	return
}

func (p *ProgramData) SetEnvironment(environment environments.Environment) (err error) {
	p.Environment = environment
	return
}

func (p *ProgramData) Id() string {
	return p.Identifier
}

func (p *ProgramData) GetEnvironment() environments.Environment {
	return p.Environment
}

func (p *ProgramData) SetAutoStart(isAutoStart bool) (err error) {
	p.RunData.AutoStart = isAutoStart
	return
}

func (p *ProgramData) IsAutoStart() (isAutoStart bool) {
	isAutoStart = p.RunData.AutoStart
	return
}

func (p *ProgramData) Save(file string) (err error) {
	logging.Debugf("Saving server %s", p.Id())

	endResult := make(map[string]interface{})
	endResult["pufferd"] = p

	data, err := json.MarshalIndent(endResult, "", "  ")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(file, data, 0664)
	return
}

func (p *ProgramData) Edit(data map[string]interface{}) (err error) {
	for k, v := range data {
		if v == nil || v == "" {
			delete(p.Data, k)
		}

		var elem DataObject

		if _, ok := p.Data[k]; ok {
			elem = p.Data[k]
		} else {
			elem = DataObject{}
		}
		elem.Value = v

		p.Data[k] = elem
	}
	err = Save(p.Id())
	return
}

func (p *ProgramData) GetData() map[string]DataObject {
	return p.Data
}

func (p *ProgramData) GetNetwork() string {
	data := p.GetData()
	ip := "0.0.0.0"
	port := "0"

	if ipData, ok := data["ip"]; ok {
		ip = ipData.Value.(string)
	}

	if portData, ok := data["port"]; ok {
		port = portData.Value.(string)
	}

	return ip + ":" + port
}<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package monitoring

import (
	"context"
	"fmt"
	"math"
	"os"
	"time"

	"golang.org/x/oauth2/google"

	. "github.com/onsi/ginkgo"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/common"
	"k8s.io/kubernetes/test/e2e/framework"
	instrumentation "k8s.io/kubernetes/test/e2e/instrumentation/common"

	gcm "google.golang.org/api/monitoring/v3"
)

var (
	// Stackdriver container metrics, as described here:
	// https://cloud.google.com/monitoring/api/metrics#gcp-container
	stackdriverMetrics = []string{
		"uptime",
		"memory/bytes_total",
		"memory/bytes_used",
		"cpu/reserved_cores",
		"cpu/usage_time",
		"memory/page_fault_count",
		"disk/bytes_used",
		"disk/bytes_total",
		"cpu/utilization",
	}

	pollFrequency = time.Second * 5
	pollTimeout   = time.Minute * 7

	rcName            = "resource-consumer"
	memoryUsed        = 64
	memoryLimit int64 = 200
	tolerance         = 0.25
)

var _ = instrumentation.SIGDescribe("Stackdriver Monitoring", func() {
	BeforeEach(func() {
		framework.SkipUnlessProviderIs("gke")
	})

	f := framework.NewDefaultFramework("stackdriver-monitoring")

	It("should have cluster metrics [Feature:StackdriverMonitoring]", func() {
		testStackdriverMonitoring(f, 1, 100, 200)
	})

})

func testStackdriverMonitoring(f *framework.Framework, pods, allPodsCPU int, perPodCPU int64) {
	projectId := framework.TestContext.CloudConfig.ProjectID

	ctx := context.Background()
	client, err := google.DefaultClient(ctx, gcm.CloudPlatformScope)
	gcmService, err := gcm.New(client)

	// set this env var if accessing Stackdriver test endpoint (default is prod):
	// $ export STACKDRIVER_API_ENDPOINT_OVERRIDE=https://test-monitoring.sandbox.googleapis.com/
	basePathOverride := os.Getenv("STACKDRIVER_API_ENDPOINT_OVERRIDE")
	if basePathOverride != "" {
		gcmService.BasePath = basePathOverride
	}

	framework.ExpectNoError(err)

	rc := common.NewDynamicResourceConsumer(rcName, common.KindDeployment, pods, allPodsCPU, memoryUsed, 0, perPodCPU, memoryLimit, f)
	defer rc.CleanUp()

	rc.WaitForReplicas(pods, 15*time.Minute)

	metricsMap := map[string]bool{}
	pollingFunction := checkForMetrics(projectId, gcmService, time.Now(), metricsMap, allPodsCPU, perPodCPU)
	err = wait.Poll(pollFrequency, pollTimeout, pollingFunction)
	if err != nil {
		framework.Logf("Missing metrics: %+v\n", metricsMap)
	}
	framework.ExpectNoError(err)
}

func checkForMetrics(projectId string, gcmService *gcm.Service, start time.Time, metricsMap map[string]bool, cpuUsed int, cpuLimit int64) func() (bool, error) {
	return func() (bool, error) {
		counter := 0
		correctUtilization := false
		for _, metric := range stackdriverMetrics {
			metricsMap[metric] = false
		}
		for _, metric := range stackdriverMetrics {
			// TODO: check only for metrics from this cluster
			ts, err := fetchTimeSeries(projectId, gcmService, metric, start, time.Now())
			framework.ExpectNoError(err)
			if len(ts) > 0 {
				counter = counter + 1
				metricsMap[metric] = true
				framework.Logf("Received %v timeseries for metric %v\n", len(ts), metric)
			} else {
				framework.Logf("No timeseries for metric %v\n", metric)
			}

			var sum float64 = 0
			switch metric {
			case "cpu/utilization":
				for _, t := range ts {
					max := t.Points[0]
					maxEnd, _ := time.Parse(time.RFC3339, max.Interval.EndTime)
					for _, p := range t.Points {
						pEnd, _ := time.Parse(time.RFC3339, p.Interval.EndTime)
						if pEnd.After(maxEnd) {
							max = p
							maxEnd, _ = time.Parse(time.RFC3339, max.Interval.EndTime)
						}
					}
					sum = sum + *max.Value.DoubleValue
					framework.Logf("Received %v points for metric %v\n",
						len(t.Points), metric)
				}
				framework.Logf("Most recent cpu/utilization sum*cpu/limit: %v\n", sum*float64(cpuLimit))
				if math.Abs(sum*float64(cpuLimit)-float64(cpuUsed)) > tolerance*float64(cpuUsed) {
					return false, nil
				} else {
					correctUtilization = true
				}
			}
		}
		if counter < 9 || !correctUtilization {
			return false, nil
		}
		return true, nil
	}
}

func createMetricFilter(metric string, container_name string) string {
	return fmt.Sprintf(`metric.type="container.googleapis.com/container/%s" AND
				resource.label.container_name="%s"`, metric, container_name)
}

func fetchTimeSeries(projectId string, gcmService *gcm.Service, metric string, start time.Time, end time.Time) ([]*gcm.TimeSeries, error) {
	response, err := gcmService.Projects.TimeSeries.
		List(fullProjectName(projectId)).
		Filter(createMetricFilter(metric, rcName)).
		IntervalStartTime(start.Format(time.RFC3339)).
		IntervalEndTime(end.Format(time.RFC3339)).
		Do()
	if err != nil {
		return nil, err
	}
	return response.TimeSeries, nil
}

func fullProjectName(name string) string {
	return fmt.Sprintf("projects/%s", name)
}
<commit_msg>Enabled SD monitoring e2e tests on GCE<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package monitoring

import (
	"context"
	"fmt"
	"math"
	"os"
	"time"

	"golang.org/x/oauth2/google"

	. "github.com/onsi/ginkgo"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/common"
	"k8s.io/kubernetes/test/e2e/framework"
	instrumentation "k8s.io/kubernetes/test/e2e/instrumentation/common"

	gcm "google.golang.org/api/monitoring/v3"
)

var (
	// Stackdriver container metrics, as described here:
	// https://cloud.google.com/monitoring/api/metrics#gcp-container
	stackdriverMetrics = []string{
		"uptime",
		"memory/bytes_total",
		"memory/bytes_used",
		"cpu/reserved_cores",
		"cpu/usage_time",
		"memory/page_fault_count",
		"disk/bytes_used",
		"disk/bytes_total",
		"cpu/utilization",
	}

	pollFrequency = time.Second * 5
	pollTimeout   = time.Minute * 7

	rcName            = "resource-consumer"
	memoryUsed        = 64
	memoryLimit int64 = 200
	tolerance         = 0.25
)

var _ = instrumentation.SIGDescribe("Stackdriver Monitoring", func() {
	BeforeEach(func() {
		framework.SkipUnlessProviderIs("gce", "gke")
	})

	f := framework.NewDefaultFramework("stackdriver-monitoring")

	It("should have cluster metrics [Feature:StackdriverMonitoring]", func() {
		testStackdriverMonitoring(f, 1, 100, 200)
	})

})

func testStackdriverMonitoring(f *framework.Framework, pods, allPodsCPU int, perPodCPU int64) {
	projectId := framework.TestContext.CloudConfig.ProjectID

	ctx := context.Background()
	client, err := google.DefaultClient(ctx, gcm.CloudPlatformScope)
	gcmService, err := gcm.New(client)

	// set this env var if accessing Stackdriver test endpoint (default is prod):
	// $ export STACKDRIVER_API_ENDPOINT_OVERRIDE=https://test-monitoring.sandbox.googleapis.com/
	basePathOverride := os.Getenv("STACKDRIVER_API_ENDPOINT_OVERRIDE")
	if basePathOverride != "" {
		gcmService.BasePath = basePathOverride
	}

	framework.ExpectNoError(err)

	rc := common.NewDynamicResourceConsumer(rcName, common.KindDeployment, pods, allPodsCPU, memoryUsed, 0, perPodCPU, memoryLimit, f)
	defer rc.CleanUp()

	rc.WaitForReplicas(pods, 15*time.Minute)

	metricsMap := map[string]bool{}
	pollingFunction := checkForMetrics(projectId, gcmService, time.Now(), metricsMap, allPodsCPU, perPodCPU)
	err = wait.Poll(pollFrequency, pollTimeout, pollingFunction)
	if err != nil {
		framework.Logf("Missing metrics: %+v\n", metricsMap)
	}
	framework.ExpectNoError(err)
}

func checkForMetrics(projectId string, gcmService *gcm.Service, start time.Time, metricsMap map[string]bool, cpuUsed int, cpuLimit int64) func() (bool, error) {
	return func() (bool, error) {
		counter := 0
		correctUtilization := false
		for _, metric := range stackdriverMetrics {
			metricsMap[metric] = false
		}
		for _, metric := range stackdriverMetrics {
			// TODO: check only for metrics from this cluster
			ts, err := fetchTimeSeries(projectId, gcmService, metric, start, time.Now())
			framework.ExpectNoError(err)
			if len(ts) > 0 {
				counter = counter + 1
				metricsMap[metric] = true
				framework.Logf("Received %v timeseries for metric %v\n", len(ts), metric)
			} else {
				framework.Logf("No timeseries for metric %v\n", metric)
			}

			var sum float64 = 0
			switch metric {
			case "cpu/utilization":
				for _, t := range ts {
					max := t.Points[0]
					maxEnd, _ := time.Parse(time.RFC3339, max.Interval.EndTime)
					for _, p := range t.Points {
						pEnd, _ := time.Parse(time.RFC3339, p.Interval.EndTime)
						if pEnd.After(maxEnd) {
							max = p
							maxEnd, _ = time.Parse(time.RFC3339, max.Interval.EndTime)
						}
					}
					sum = sum + *max.Value.DoubleValue
					framework.Logf("Received %v points for metric %v\n",
						len(t.Points), metric)
				}
				framework.Logf("Most recent cpu/utilization sum*cpu/limit: %v\n", sum*float64(cpuLimit))
				if math.Abs(sum*float64(cpuLimit)-float64(cpuUsed)) > tolerance*float64(cpuUsed) {
					return false, nil
				} else {
					correctUtilization = true
				}
			}
		}
		if counter < 9 || !correctUtilization {
			return false, nil
		}
		return true, nil
	}
}

func createMetricFilter(metric string, container_name string) string {
	return fmt.Sprintf(`metric.type="container.googleapis.com/container/%s" AND
				resource.label.container_name="%s"`, metric, container_name)
}

func fetchTimeSeries(projectId string, gcmService *gcm.Service, metric string, start time.Time, end time.Time) ([]*gcm.TimeSeries, error) {
	response, err := gcmService.Projects.TimeSeries.
		List(fullProjectName(projectId)).
		Filter(createMetricFilter(metric, rcName)).
		IntervalStartTime(start.Format(time.RFC3339)).
		IntervalEndTime(end.Format(time.RFC3339)).
		Do()
	if err != nil {
		return nil, err
	}
	return response.TimeSeries, nil
}

func fullProjectName(name string) string {
	return fmt.Sprintf("projects/%s", name)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"strings"
	"text/template"
	"time"
)

var cjsTemplate *template.Template

func init() {
	cjsTemplateString :=
		`{
    type: '{{ .ChartType }}',{{ $manyColor := or (eq .ChartType "pie") (eq .ChartType "bar") }}
    data: {
      labels: [{{ .Labels }}],
      datasets: [
        {{range $i,$v := .Datasets}}{{if $i}},{{end -}}
        {
          fill: {{ .Fill }},
          {{if len .Label}}label: '{{ .Label }}',{{end}}
          {{if len .BackgroundColor}}backgroundColor: {{if $manyColor}}[{{end}}{{ .BackgroundColor }}{{if $manyColor}}]{{end}},{{end}}
          {{if len .BorderColor}}borderColor: {{ .BorderColor }},{{end}}
          data: [
            {{if len .SimpleData}}{{range $i,$v := .SimpleData}}{{if $i}},{{end -}}{{.}}{{end}}{{end}}
            {{if len .ComplexData}}{{range $i,$v := .ComplexData}}{{if $i}},{{end -}}
              {
                x: {{ .X }},
                y: {{ .Y -}}
                {{- if .UsesR}},
                r: {{ .R }}
                {{end}}
              }
            {{end}}{{end}}
          ]
        }
        {{end}}
      ]
    },
    options: {
      title: {
            display: {{ if len .Title }}true{{else}}false{{end}},
            text: '{{ .Title }}'
        },
        tooltips: {
            callbacks: {
                label: function(tti, data) {
                  {{ .TooltipCallback }}
                }
            }
        }
        {{ if ne .ChartType "pie" }},
          legend: {
              display: false
          },
          scales: {
              yAxes: [{
                  type: "{{ .ScaleType }}",
                  ticks: {
                      beginAtZero: {{ .ZeroBased }},
                      callback: function(value, index, values) {
                          return value;
                      }
                  },
                  scaleLabel: {
                      display: {{if eq .YLabel ""}}false{{else}}true{{end}},
                      labelString: '{{ .YLabel }}'
                  }
              }],
              xAxes: [{
                  {{ if .UsesTimeScale }}
                  type: 'time',
                  position: 'bottom',
                  {{end}}
                  scaleLabel: {
                      display: {{if eq .XLabel ""}}false{{else}}true{{end}},
                      labelString: '{{ .XLabel }}'
                  }
              }]
          }
        {{end}}
    }
}`

	var err error
	cjsTemplate, err = template.New("").Parse(cjsTemplateString)
	if err != nil {
		log.Fatal(err)
	}
}

type inData struct {
	ChartType string
	FSS       [][]float64
	TSS       [][]time.Time
	SSS       [][]string
	MinFSS    []float64
	MaxFSS    []float64
	Title     string
	ScaleType string
	XLabel    string
	YLabel    string
	ZeroBased bool
}

func (i inData) hasFloats() bool  { return len(i.FSS) > 0 }
func (i inData) hasStrings() bool { return len(i.SSS) > 0 }
func (i inData) hasTimes() bool   { return len(i.TSS) > 0 }

type cjsChart struct {
	inData inData
}

func (c cjsChart) chart() (interface{}, *template.Template, error) {
	return c.data(), cjsTemplate, nil
}

type cjsData struct {
	ChartType       string
	Title           string
	ScaleType       string
	XLabel          string
	YLabel          string
	ZeroBased       bool
	Labels          string // Need backticks; can't use array
	Datasets        []cjsDataset
	TooltipCallback string
	UsesTimeScale   bool
}

type cjsDataset struct {
	SimpleData      []string
	ComplexData     []cjsDataPoint
	BackgroundColor string
	Fill            bool
	Label           string
	BorderColor     string
}

type cjsDataPoint struct {
	X, Y, R string
	UsesR   bool
}

func (c cjsChart) data() cjsData {
	d := c.labelsAndDatasets()
	d.ChartType = c.inData.ChartType
	d.Title = c.inData.Title
	d.ScaleType = c.inData.ScaleType
	d.XLabel = c.inData.XLabel
	d.YLabel = c.inData.YLabel
	d.ZeroBased = c.inData.ZeroBased
	d.TooltipCallback = c.tooltipCallback()
	switch d.ChartType {
	case "scatterline":
		d.ChartType = "line"
	case "scatter":
		d.ChartType = "bubble"
	}

	return d
}

func (c cjsChart) labelsAndDatasets() cjsData {
	var usesTimeScale bool
	if c.inData.ChartType == "line" && (!c.inData.hasStrings() || c.inData.hasTimes()) {
		c.inData.ChartType = "scatterline"
	}
	switch c.inData.ChartType {
	case "pie", "bar":
		return cjsData{
			Labels: c.marshalLabels(),
			Datasets: []cjsDataset{{
				Fill:            true,
				SimpleData:      c.marshalSimpleData(0),
				BackgroundColor: colorFirstN(len(c.inData.FSS)),
			}},
		}
	case "line":
		ds := []cjsDataset{}
		for i := range c.inData.FSS[0] {
			ds = append(ds, cjsDataset{
				Fill:        false,
				SimpleData:  c.marshalSimpleData(i),
				BorderColor: colorIndex(i),
			})
		}
		return cjsData{
			Labels:   c.marshalLabels(),
			Datasets: ds,
		}
	case "scatterline":
		dss := []cjsDataset{}
		for n := range c.inData.FSS[0] {
			ds := []cjsDataPoint{}
			for i := range c.inData.FSS {
				d := cjsDataPoint{}
				if c.inData.hasTimes() {
					usesTimeScale = true
					d.X = "'" + c.inData.TSS[i][0].Format("2006-01-02T15:04:05.999999999") + "'"
					d.Y = fmt.Sprintf("%g", c.inData.FSS[i][n])
				} else {
					d.X = fmt.Sprintf("%g", c.inData.FSS[i][0])
					d.Y = fmt.Sprintf("%g", c.inData.FSS[i][n+1])
				}
				ds = append(ds, d)
			}
			dss = append(dss, cjsDataset{
				Fill:        false,
				Label:       fmt.Sprintf("column %v", n),
				ComplexData: ds,
				BorderColor: colorIndex(n),
			})
		}
		return cjsData{
			Datasets:      dss,
			UsesTimeScale: usesTimeScale,
		}
	case "scatter":
		css := map[string]string{}
		colorReset()
		for _, ss := range c.inData.SSS {
			if len(ss) > 0 && css[ss[0]] == "" {
				css[ss[0]] = colorNext()
			}
		}

		dss := []cjsDataset{}
		for i := range c.inData.FSS {
			d := cjsDataPoint{UsesR: true}
			if c.inData.hasTimes() {
				usesTimeScale = true
				d.X = "'" + c.inData.TSS[i][0].Format("2006-01-02T15:04:05.999999999") + "'"
				d.Y = fmt.Sprintf("%g", c.inData.FSS[i][0])
				if len(c.inData.FSS[i]) >= 2 {
					d.R = fmt.Sprintf("%v", scatterRadius(c.inData.FSS[i][1], c.inData.MinFSS[1], c.inData.MaxFSS[1]))
				} else {
					d.R = fmt.Sprintf("%v", 4)
				}
			} else {
				d.X = fmt.Sprintf("%g", c.inData.FSS[i][0])
				d.Y = fmt.Sprintf("%g", c.inData.FSS[i][1])
				if len(c.inData.FSS[i]) >= 3 {
					d.R = fmt.Sprintf("%v", scatterRadius(c.inData.FSS[i][2], c.inData.MinFSS[2], c.inData.MaxFSS[2]))
				} else {
					d.R = fmt.Sprintf("%v", 4)
				}
			}
			color := colorFirstN(1)
			label := ""
			if c.inData.hasStrings() {
				color = css[c.inData.SSS[i][0]]
				label = c.inData.SSS[i][0]
			}
			dss = append(dss, cjsDataset{
				Fill:            true,
				Label:           label,
				ComplexData:     []cjsDataPoint{d},
				BackgroundColor: color,
			})
		}
		return cjsData{
			Datasets:      dss,
			UsesTimeScale: usesTimeScale,
		}
	default:
		log.Fatalf("Unknown chart type: %v", c.inData.ChartType)
		return cjsData{}
	}
}

func (c cjsChart) marshalLabels() string {
	if !c.inData.hasStrings() && c.inData.hasTimes() {
		ls := make([]string, len(c.inData.TSS))
		for i, ts := range c.inData.TSS {
			ls[i] = ts[0].Format("2006-01-02T15:04:05.999999999")
		}
		return "`" + strings.Join(ls, "`,`") + "`"
	}

	if !c.inData.hasStrings() {
		ls := make([]string, len(c.inData.FSS))
		for i := range c.inData.FSS {
			ls[i] = fmt.Sprintf("slice %v", i)
		}
		return strings.Join(ls, ",")
	}

	ls := make([]string, len(c.inData.SSS))
	for i, l := range c.inData.SSS {
		ls[i] = preprocessLabel(l[0])
	}
	return strings.Join(ls, ",")
}

func (c cjsChart) marshalSimpleData(col int) []string {
	ds := make([]string, len(c.inData.FSS))
	for i, f := range c.inData.FSS {
		ds[i] = fmt.Sprintf("%g", f[col])
	}
	return ds
}

func (c cjsChart) tooltipCallback() string {
	switch c.inData.ChartType {
	case "pie":
		return `
                    var value = data.datasets[0].data[tti.index];
                    var total = data.datasets[0].data.reduce((a, b) => a + b, 0)
                    var label = data.labels[tti.index];
                    var percentage = Math.round(value / total * 100);
                    return label + ': ' + percentage + '%';
    `
	case "line", "scatterline":
		return `
                    var value = data.datasets[tti.datasetIndex].data[tti.index];
                    if (value.y) {
                        value = value.y
                    }
                    return value;
    `
	case "scatter":
		return `
                    var value = data.datasets[tti.datasetIndex].data[tti.index];
                    var label = data.datasets[tti.datasetIndex].label;
                    return (label ? label + ': ' : '') + '(' + value.x + ', ' + value.y + ')';
    `
	case "bar":
		return `
                    var value = data.datasets[0].data[tti.index];
                    var label = data.labels[tti.index];
                    return value;
    `
	default:
		return ``
	}
}

func scatterRadius(x, min, max float64) float64 {
	if max-min < 50 {
		return x - min + 4
	}
	return float64(4) + (x-min)/(max-min)*50
}
<commit_msg>Defaults to 0 on scatter when no Y series available.<commit_after>package main

import (
	"fmt"
	"log"
	"strings"
	"text/template"
	"time"
)

var cjsTemplate *template.Template

func init() {
	cjsTemplateString :=
		`{
    type: '{{ .ChartType }}',{{ $manyColor := or (eq .ChartType "pie") (eq .ChartType "bar") }}
    data: {
      labels: [{{ .Labels }}],
      datasets: [
        {{range $i,$v := .Datasets}}{{if $i}},{{end -}}
        {
          fill: {{ .Fill }},
          {{if len .Label}}label: '{{ .Label }}',{{end}}
          {{if len .BackgroundColor}}backgroundColor: {{if $manyColor}}[{{end}}{{ .BackgroundColor }}{{if $manyColor}}]{{end}},{{end}}
          {{if len .BorderColor}}borderColor: {{ .BorderColor }},{{end}}
          data: [
            {{if len .SimpleData}}{{range $i,$v := .SimpleData}}{{if $i}},{{end -}}{{.}}{{end}}{{end}}
            {{if len .ComplexData}}{{range $i,$v := .ComplexData}}{{if $i}},{{end -}}
              {
                x: {{ .X }},
                y: {{ .Y -}}
                {{- if .UsesR}},
                r: {{ .R }}
                {{end}}
              }
            {{end}}{{end}}
          ]
        }
        {{end}}
      ]
    },
    options: {
      title: {
            display: {{ if len .Title }}true{{else}}false{{end}},
            text: '{{ .Title }}'
        },
        tooltips: {
            callbacks: {
                label: function(tti, data) {
                  {{ .TooltipCallback }}
                }
            }
        }
        {{ if ne .ChartType "pie" }},
          legend: {
              display: false
          },
          scales: {
              yAxes: [{
                  type: "{{ .ScaleType }}",
                  ticks: {
                      beginAtZero: {{ .ZeroBased }},
                      callback: function(value, index, values) {
                          return value;
                      }
                  },
                  scaleLabel: {
                      display: {{if eq .YLabel ""}}false{{else}}true{{end}},
                      labelString: '{{ .YLabel }}'
                  }
              }],
              xAxes: [{
                  {{ if .UsesTimeScale }}
                  type: 'time',
                  position: 'bottom',
                  {{end}}
                  scaleLabel: {
                      display: {{if eq .XLabel ""}}false{{else}}true{{end}},
                      labelString: '{{ .XLabel }}'
                  }
              }]
          }
        {{end}}
    }
}`

	var err error
	cjsTemplate, err = template.New("").Parse(cjsTemplateString)
	if err != nil {
		log.Fatal(err)
	}
}

type inData struct {
	ChartType string
	FSS       [][]float64
	TSS       [][]time.Time
	SSS       [][]string
	MinFSS    []float64
	MaxFSS    []float64
	Title     string
	ScaleType string
	XLabel    string
	YLabel    string
	ZeroBased bool
}

func (i inData) hasFloats() bool  { return len(i.FSS) > 0 }
func (i inData) hasStrings() bool { return len(i.SSS) > 0 }
func (i inData) hasTimes() bool   { return len(i.TSS) > 0 }

type cjsChart struct {
	inData inData
}

func (c cjsChart) chart() (interface{}, *template.Template, error) {
	return c.data(), cjsTemplate, nil
}

type cjsData struct {
	ChartType       string
	Title           string
	ScaleType       string
	XLabel          string
	YLabel          string
	ZeroBased       bool
	Labels          string // Need backticks; can't use array
	Datasets        []cjsDataset
	TooltipCallback string
	UsesTimeScale   bool
}

type cjsDataset struct {
	SimpleData      []string
	ComplexData     []cjsDataPoint
	BackgroundColor string
	Fill            bool
	Label           string
	BorderColor     string
}

type cjsDataPoint struct {
	X, Y, R string
	UsesR   bool
}

func (c cjsChart) data() cjsData {
	d := c.labelsAndDatasets()
	d.ChartType = c.inData.ChartType
	d.Title = c.inData.Title
	d.ScaleType = c.inData.ScaleType
	d.XLabel = c.inData.XLabel
	d.YLabel = c.inData.YLabel
	d.ZeroBased = c.inData.ZeroBased
	d.TooltipCallback = c.tooltipCallback()
	switch d.ChartType {
	case "scatterline":
		d.ChartType = "line"
	case "scatter":
		d.ChartType = "bubble"
	}

	return d
}

func (c cjsChart) labelsAndDatasets() cjsData {
	var usesTimeScale bool
	if c.inData.ChartType == "line" && (!c.inData.hasStrings() || c.inData.hasTimes()) {
		c.inData.ChartType = "scatterline"
	}
	switch c.inData.ChartType {
	case "pie", "bar":
		return cjsData{
			Labels: c.marshalLabels(),
			Datasets: []cjsDataset{{
				Fill:            true,
				SimpleData:      c.marshalSimpleData(0),
				BackgroundColor: colorFirstN(len(c.inData.FSS)),
			}},
		}
	case "line":
		ds := []cjsDataset{}
		for i := range c.inData.FSS[0] {
			ds = append(ds, cjsDataset{
				Fill:        false,
				SimpleData:  c.marshalSimpleData(i),
				BorderColor: colorIndex(i),
			})
		}
		return cjsData{
			Labels:   c.marshalLabels(),
			Datasets: ds,
		}
	case "scatterline":
		dss := []cjsDataset{}
		for n := range c.inData.FSS[0] {
			ds := []cjsDataPoint{}
			for i := range c.inData.FSS {
				d := cjsDataPoint{}
				if c.inData.hasTimes() {
					usesTimeScale = true
					d.X = "'" + c.inData.TSS[i][0].Format("2006-01-02T15:04:05.999999999") + "'"
					d.Y = fmt.Sprintf("%g", c.inData.FSS[i][n])
				} else {
					d.X = fmt.Sprintf("%g", c.inData.FSS[i][0])
					d.Y = fmt.Sprintf("%g", c.inData.FSS[i][n+1])
				}
				ds = append(ds, d)
			}
			dss = append(dss, cjsDataset{
				Fill:        false,
				Label:       fmt.Sprintf("column %v", n),
				ComplexData: ds,
				BorderColor: colorIndex(n),
			})
		}
		return cjsData{
			Datasets:      dss,
			UsesTimeScale: usesTimeScale,
		}
	case "scatter":
		css := map[string]string{}
		colorReset()
		for _, ss := range c.inData.SSS {
			if len(ss) > 0 && css[ss[0]] == "" {
				css[ss[0]] = colorNext()
			}
		}

		dss := []cjsDataset{}
		for i := range c.inData.FSS {
			d := cjsDataPoint{UsesR: true}
			if c.inData.hasTimes() {
				usesTimeScale = true
				d.X = "'" + c.inData.TSS[i][0].Format("2006-01-02T15:04:05.999999999") + "'"
				d.Y = fmt.Sprintf("%g", c.inData.FSS[i][0])
				if len(c.inData.FSS[i]) >= 2 {
					d.R = fmt.Sprintf("%v", scatterRadius(c.inData.FSS[i][1], c.inData.MinFSS[1], c.inData.MaxFSS[1]))
				} else {
					d.R = fmt.Sprintf("%v", 4)
				}
			} else {
				d.X = fmt.Sprintf("%g", c.inData.FSS[i][0])
				d.Y = "0"
				if len(c.inData.FSS[i]) >= 2 {
					d.Y = fmt.Sprintf("%g", c.inData.FSS[i][1])
				}
				if len(c.inData.FSS[i]) >= 3 {
					d.R = fmt.Sprintf("%v", scatterRadius(c.inData.FSS[i][2], c.inData.MinFSS[2], c.inData.MaxFSS[2]))
				} else {
					d.R = fmt.Sprintf("%v", 4)
				}
			}
			color := colorFirstN(1)
			label := ""
			if c.inData.hasStrings() {
				color = css[c.inData.SSS[i][0]]
				label = c.inData.SSS[i][0]
			}
			dss = append(dss, cjsDataset{
				Fill:            true,
				Label:           label,
				ComplexData:     []cjsDataPoint{d},
				BackgroundColor: color,
			})
		}
		return cjsData{
			Datasets:      dss,
			UsesTimeScale: usesTimeScale,
		}
	default:
		log.Fatalf("Unknown chart type: %v", c.inData.ChartType)
		return cjsData{}
	}
}

func (c cjsChart) marshalLabels() string {
	if !c.inData.hasStrings() && c.inData.hasTimes() {
		ls := make([]string, len(c.inData.TSS))
		for i, ts := range c.inData.TSS {
			ls[i] = ts[0].Format("2006-01-02T15:04:05.999999999")
		}
		return "`" + strings.Join(ls, "`,`") + "`"
	}

	if !c.inData.hasStrings() {
		ls := make([]string, len(c.inData.FSS))
		for i := range c.inData.FSS {
			ls[i] = fmt.Sprintf("slice %v", i)
		}
		return strings.Join(ls, ",")
	}

	ls := make([]string, len(c.inData.SSS))
	for i, l := range c.inData.SSS {
		ls[i] = preprocessLabel(l[0])
	}
	return strings.Join(ls, ",")
}

func (c cjsChart) marshalSimpleData(col int) []string {
	ds := make([]string, len(c.inData.FSS))
	for i, f := range c.inData.FSS {
		ds[i] = fmt.Sprintf("%g", f[col])
	}
	return ds
}

func (c cjsChart) tooltipCallback() string {
	switch c.inData.ChartType {
	case "pie":
		return `
                    var value = data.datasets[0].data[tti.index];
                    var total = data.datasets[0].data.reduce((a, b) => a + b, 0)
                    var label = data.labels[tti.index];
                    var percentage = Math.round(value / total * 100);
                    return label + ': ' + percentage + '%';
    `
	case "line", "scatterline":
		return `
                    var value = data.datasets[tti.datasetIndex].data[tti.index];
                    if (value.y) {
                        value = value.y
                    }
                    return value;
    `
	case "scatter":
		return `
                    var value = data.datasets[tti.datasetIndex].data[tti.index];
                    var label = data.datasets[tti.datasetIndex].label;
                    return (label ? label + ': ' : '') + '(' + value.x + ', ' + value.y + ')';
    `
	case "bar":
		return `
                    var value = data.datasets[0].data[tti.index];
                    var label = data.labels[tti.index];
                    return value;
    `
	default:
		return ``
	}
}

func scatterRadius(x, min, max float64) float64 {
	if max-min < 50 {
		return x - min + 4
	}
	return float64(4) + (x-min)/(max-min)*50
}
<|endoftext|>


<commit_before>package generator

import (
	"fmt"
	"io"
	"strings"
	"time"
)

func genLabel() string {
	tpl := "WARNING: This file has automatically been generated on %s.\nBy http://git.io/cgogen. DO NOT EDIT."
	return fmt.Sprintf(tpl, time.Now().Format(time.RFC1123))
}

func (gen *Generator) WriteDoc(wr io.Writer) bool {
	var hasDoc bool
	if len(gen.cfg.PackageLicense) > 0 {
		writeTextBlock(wr, gen.cfg.PackageLicense)
		writeSpace(wr, 1)
		hasDoc = true
	}
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	if len(gen.cfg.PackageDescription) > 0 {
		writeLongTextBlock(wr, gen.cfg.PackageDescription)
		hasDoc = true
	}
	writePackageName(wr, gen.pkg)
	writeSpace(wr, 1)
	return hasDoc
}

func (gen *Generator) WriteIncludes(wr io.Writer) {
	writeStartComment(wr)
	writePkgConfig(wr, gen.cfg.PkgConfigOpts)
	writeFlagSet(wr, gen.cfg.CPPFlags)
	writeFlagSet(wr, gen.cfg.CXXFlags)
	writeFlagSet(wr, gen.cfg.CFlags)
	writeFlagSet(wr, gen.cfg.LDFlags)
	for _, path := range gen.cfg.SysIncludes {
		writeSysInclude(wr, path)
	}
	for _, path := range gen.cfg.Includes {
		writeInclude(wr, path)
	}
	writeCStdIncludes(wr, gen.cfg.SysIncludes)
	fmt.Fprintln(wr, `#include "cgo_helpers.h"`)
	writeEndComment(wr)
	fmt.Fprintln(wr, `import "C"`)
	writeSpace(wr, 1)
}

func hasLib(paths []string, lib string) bool {
	for i := range paths {
		if paths[i] == lib {
			return true
		}
	}
	return false
}

func (gen *Generator) writeGoHelpersHeader(wr io.Writer) {
	writeTextBlock(wr, gen.cfg.PackageLicense)
	writeSpace(wr, 1)
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	writePackageName(wr, gen.pkg)
	writeSpace(wr, 1)
	gen.WriteIncludes(wr)
}

func (gen *Generator) writeCHHelpersHeader(wr io.Writer) {
	if len(gen.cfg.PackageLicense) > 0 {
		writeTextBlock(wr, gen.cfg.PackageLicense)
		writeSpace(wr, 1)
	}
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	for _, path := range gen.cfg.SysIncludes {
		writeSysInclude(wr, path)
	}
	for _, path := range gen.cfg.Includes {
		writeInclude(wr, path)
	}
	writeCStdIncludes(wr, gen.cfg.SysIncludes)
	writeCHPragma(wr)
	writeSpace(wr, 1)
}

func (gen *Generator) writeCCHelpersHeader(wr io.Writer) {
	if len(gen.cfg.PackageLicense) > 0 {
		writeTextBlock(wr, gen.cfg.PackageLicense)
		writeSpace(wr, 1)
	}
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	writeCGOIncludes(wr)
	writeSpace(wr, 1)
}

func writeCGOIncludes(wr io.Writer) {
	fmt.Fprintln(wr, `#include "_cgo_export.h"`)
	fmt.Fprintln(wr, `#include "cgo_helpers.h"`)
}

func writeCHPragma(wr io.Writer) {
	fmt.Fprintln(wr, "#pragma once")
}

func writeCStdIncludes(wr io.Writer, sysIncludes []string) {
	if !hasLib(sysIncludes, "stdlib.h") {
		fmt.Fprintln(wr, "#include <stdlib.h>")
	}
	// if !hasLib(sysIncludes, "stdbool.h") {
	// 	fmt.Fprintln(wr, "#include <stdbool.h>")
	// }
}

func (gen *Generator) WritePackageHeader(wr io.Writer) {
	writeTextBlock(wr, gen.cfg.PackageLicense)
	writeSpace(wr, 1)
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	writePackageName(wr, gen.pkg)
	writeSpace(wr, 1)
}

func writeFlagSet(wr io.Writer, flags ArchFlagSet) {
	if len(flags.Name) == 0 {
		return
	}
	if len(flags.Flags) == 0 {
		return
	}
	fmt.Fprintf(wr, "#cgo %s %s: %s\n",
		strings.Join(flags.Arch, ","),
		flags.Name,
		strings.Join(flags.Flags, " "),
	)
}

func writeSysInclude(wr io.Writer, path string) {
	fmt.Fprintf(wr, "#include <%s>\n", path)
}

func writeInclude(wr io.Writer, path string) {
	fmt.Fprintf(wr, "#include \"%s\"\n", path)
}

func writePkgConfig(wr io.Writer, opts []string) {
	if len(opts) == 0 {
		return
	}
	fmt.Fprintf(wr, "#cgo pkg-config: %s\n", strings.Join(opts, " "))
}

func writeStartComment(wr io.Writer) {
	fmt.Fprintln(wr, "/*")
}

func writeEndComment(wr io.Writer) {
	fmt.Fprintln(wr, "*/")
}

func writePackageName(wr io.Writer, name string) {
	if len(name) == 0 {
		name = "main"
	}
	fmt.Fprintf(wr, "package %s\n", name)
}

func writeLongTextBlock(wr io.Writer, text string) {
	if len(text) == 0 {
		return
	}
	writeStartComment(wr)
	fmt.Fprint(wr, text)
	writeSpace(wr, 1)
	writeEndComment(wr)
}

func writeTextBlock(wr io.Writer, text string) {
	if len(text) == 0 {
		return
	}
	lines := strings.Split(text, "\n")
	for _, line := range lines {
		fmt.Fprintf(wr, "// %s\n", line)
	}
}

func writeSourceBlock(wr io.Writer, src string) {
	if len(src) == 0 {
		return
	}
	fmt.Fprint(wr, src)
	writeSpace(wr, 1)
}
<commit_msg>Fix contrains in ArchFlags.<commit_after>package generator

import (
	"fmt"
	"io"
	"strings"
	"time"
)

func genLabel() string {
	tpl := "WARNING: This file has automatically been generated on %s.\nBy http://git.io/cgogen. DO NOT EDIT."
	return fmt.Sprintf(tpl, time.Now().Format(time.RFC1123))
}

func (gen *Generator) WriteDoc(wr io.Writer) bool {
	var hasDoc bool
	if len(gen.cfg.PackageLicense) > 0 {
		writeTextBlock(wr, gen.cfg.PackageLicense)
		writeSpace(wr, 1)
		hasDoc = true
	}
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	if len(gen.cfg.PackageDescription) > 0 {
		writeLongTextBlock(wr, gen.cfg.PackageDescription)
		hasDoc = true
	}
	writePackageName(wr, gen.pkg)
	writeSpace(wr, 1)
	return hasDoc
}

func (gen *Generator) WriteIncludes(wr io.Writer) {
	writeStartComment(wr)
	writePkgConfig(wr, gen.cfg.PkgConfigOpts)
	writeFlagSet(wr, gen.cfg.CPPFlags)
	writeFlagSet(wr, gen.cfg.CXXFlags)
	writeFlagSet(wr, gen.cfg.CFlags)
	writeFlagSet(wr, gen.cfg.LDFlags)
	for _, path := range gen.cfg.SysIncludes {
		writeSysInclude(wr, path)
	}
	for _, path := range gen.cfg.Includes {
		writeInclude(wr, path)
	}
	writeCStdIncludes(wr, gen.cfg.SysIncludes)
	fmt.Fprintln(wr, `#include "cgo_helpers.h"`)
	writeEndComment(wr)
	fmt.Fprintln(wr, `import "C"`)
	writeSpace(wr, 1)
}

func hasLib(paths []string, lib string) bool {
	for i := range paths {
		if paths[i] == lib {
			return true
		}
	}
	return false
}

func (gen *Generator) writeGoHelpersHeader(wr io.Writer) {
	writeTextBlock(wr, gen.cfg.PackageLicense)
	writeSpace(wr, 1)
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	writePackageName(wr, gen.pkg)
	writeSpace(wr, 1)
	gen.WriteIncludes(wr)
}

func (gen *Generator) writeCHHelpersHeader(wr io.Writer) {
	if len(gen.cfg.PackageLicense) > 0 {
		writeTextBlock(wr, gen.cfg.PackageLicense)
		writeSpace(wr, 1)
	}
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	for _, path := range gen.cfg.SysIncludes {
		writeSysInclude(wr, path)
	}
	for _, path := range gen.cfg.Includes {
		writeInclude(wr, path)
	}
	writeCStdIncludes(wr, gen.cfg.SysIncludes)
	writeCHPragma(wr)
	writeSpace(wr, 1)
}

func (gen *Generator) writeCCHelpersHeader(wr io.Writer) {
	if len(gen.cfg.PackageLicense) > 0 {
		writeTextBlock(wr, gen.cfg.PackageLicense)
		writeSpace(wr, 1)
	}
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	writeCGOIncludes(wr)
	writeSpace(wr, 1)
}

func writeCGOIncludes(wr io.Writer) {
	fmt.Fprintln(wr, `#include "_cgo_export.h"`)
	fmt.Fprintln(wr, `#include "cgo_helpers.h"`)
}

func writeCHPragma(wr io.Writer) {
	fmt.Fprintln(wr, "#pragma once")
}

func writeCStdIncludes(wr io.Writer, sysIncludes []string) {
	if !hasLib(sysIncludes, "stdlib.h") {
		fmt.Fprintln(wr, "#include <stdlib.h>")
	}
	// if !hasLib(sysIncludes, "stdbool.h") {
	// 	fmt.Fprintln(wr, "#include <stdbool.h>")
	// }
}

func (gen *Generator) WritePackageHeader(wr io.Writer) {
	writeTextBlock(wr, gen.cfg.PackageLicense)
	writeSpace(wr, 1)
	writeTextBlock(wr, genLabel())
	writeSpace(wr, 1)
	writePackageName(wr, gen.pkg)
	writeSpace(wr, 1)
}

func writeFlagSet(wr io.Writer, flags ArchFlagSet) {
	if len(flags.Name) == 0 {
		return
	}
	if len(flags.Flags) == 0 {
		return
	}
	if len(flags.Arch) == 0 {
		fmt.Fprintf(wr, "#cgo %s: %s\n", flags.Name, strings.Join(flags.Flags, " "))
		return
	}
	constrains := strings.Join(flags.Arch, " ")
	fmt.Fprintf(wr, "#cgo %s %s: %s\n", constrains, flags.Name, strings.Join(flags.Flags, " "))
}

func writeSysInclude(wr io.Writer, path string) {
	fmt.Fprintf(wr, "#include <%s>\n", path)
}

func writeInclude(wr io.Writer, path string) {
	fmt.Fprintf(wr, "#include \"%s\"\n", path)
}

func writePkgConfig(wr io.Writer, opts []string) {
	if len(opts) == 0 {
		return
	}
	fmt.Fprintf(wr, "#cgo pkg-config: %s\n", strings.Join(opts, " "))
}

func writeStartComment(wr io.Writer) {
	fmt.Fprintln(wr, "/*")
}

func writeEndComment(wr io.Writer) {
	fmt.Fprintln(wr, "*/")
}

func writePackageName(wr io.Writer, name string) {
	if len(name) == 0 {
		name = "main"
	}
	fmt.Fprintf(wr, "package %s\n", name)
}

func writeLongTextBlock(wr io.Writer, text string) {
	if len(text) == 0 {
		return
	}
	writeStartComment(wr)
	fmt.Fprint(wr, text)
	writeSpace(wr, 1)
	writeEndComment(wr)
}

func writeTextBlock(wr io.Writer, text string) {
	if len(text) == 0 {
		return
	}
	lines := strings.Split(text, "\n")
	for _, line := range lines {
		fmt.Fprintf(wr, "// %s\n", line)
	}
}

func writeSourceBlock(wr io.Writer, src string) {
	if len(src) == 0 {
		return
	}
	fmt.Fprint(wr, src)
	writeSpace(wr, 1)
}
<|endoftext|>


<commit_before>package raft

import (
	"github.com/hashicorp/raft"
	"github.com/travisjeffery/simplelog"
)

type OptionFn func(b *Raft)

func Logger(logger *simplelog.Logger) OptionFn {
	return func(b *Raft) {
		b.logger = logger
	}
}

func DataDir(dataDir string) OptionFn {
	return func(b *Raft) {
		b.dataDir = dataDir
	}
}

func Addr(raftAddr string) OptionFn {
	return func(b *Raft) {
		b.addr = raftAddr
	}
}

func Config(raft *raft.Config) OptionFn {
	return func(b *Raft) {
		b.config = raft
	}
}
<commit_msg>raft: rename var<commit_after>package raft

import (
	"github.com/hashicorp/raft"
	"github.com/travisjeffery/simplelog"
)

type OptionFn func(b *Raft)

func Logger(logger *simplelog.Logger) OptionFn {
	return func(b *Raft) {
		b.logger = logger
	}
}

func DataDir(dataDir string) OptionFn {
	return func(b *Raft) {
		b.dataDir = dataDir
	}
}

func Addr(addr string) OptionFn {
	return func(b *Raft) {
		b.addr = addr
	}
}

func Config(raft *raft.Config) OptionFn {
	return func(b *Raft) {
		b.config = raft
	}
}
<|endoftext|>