<commit_before>package command

import (
	"fmt"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"time"

	stats_collect "github.com/chrislusf/seaweedfs/weed/stats"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/util"
)

type ServerOptions struct {
	cpuprofile *string
	v          VolumeServerOptions
}

var (
	serverOptions    ServerOptions
	masterOptions    MasterOptions
	filerOptions     FilerOptions
	s3Options        S3Options
	msgBrokerOptions MessageBrokerOptions
)

func init() {
	cmdServer.Run = runServer // break init cycle
}

var cmdServer = &Command{
	UsageLine: "server -dir=/tmp -volume.max=5 -ip=server_name",
	Short:     "start a master server, a volume server, and optionally a filer and a S3 gateway",
	Long: `start both a volume server to provide storage spaces
  and a master server to provide volume=>location mapping service and sequence number of file ids

  This is provided as a convenient way to start both volume server and master server.
  The servers acts exactly the same as starting them separately.
  So other volume servers can connect to this master server also.

  Optionally, a filer server can be started.
  Also optionally, a S3 gateway can be started.

  `,
}

var (
	serverIp                  = cmdServer.Flag.String("ip", util.DetectedHostAddress(), "ip or server name")
	serverBindIp              = cmdServer.Flag.String("ip.bind", "0.0.0.0", "ip address to bind to")
	serverTimeout             = cmdServer.Flag.Int("idleTimeout", 30, "connection idle seconds")
	serverDataCenter          = cmdServer.Flag.String("dataCenter", "", "current volume server's data center name")
	serverRack                = cmdServer.Flag.String("rack", "", "current volume server's rack name")
	serverWhiteListOption     = cmdServer.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")
	serverDisableHttp         = cmdServer.Flag.Bool("disableHttp", false, "disable http requests, only gRPC operations are allowed.")
	volumeDataFolders         = cmdServer.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
	volumeMaxDataVolumeCounts = cmdServer.Flag.String("volume.max", "8", "maximum numbers of volumes, count[,count]... If set to zero, the limit will be auto configured.")
	volumeMinFreeSpacePercent = cmdServer.Flag.String("volume.minFreeSpacePercent", "1", "minimum free disk space (default to 1%). Low disk space will mark all volumes as ReadOnly.")
	serverMetricsHttpPort     = cmdServer.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")

	// pulseSeconds              = cmdServer.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
	isStartingVolumeServer = cmdServer.Flag.Bool("volume", true, "whether to start volume server")
	isStartingFiler        = cmdServer.Flag.Bool("filer", false, "whether to start filer")
	isStartingS3           = cmdServer.Flag.Bool("s3", false, "whether to start S3 gateway")
	isStartingMsgBroker    = cmdServer.Flag.Bool("msgBroker", false, "whether to start message broker")

	serverWhiteList []string

	False = false
)

func init() {
	serverOptions.cpuprofile = cmdServer.Flag.String("cpuprofile", "", "cpu profile output file")

	masterOptions.port = cmdServer.Flag.Int("master.port", 9333, "master server http listen port")
	masterOptions.metaFolder = cmdServer.Flag.String("master.dir", "", "data directory to store meta data, default to same as -dir specified")
	masterOptions.peers = cmdServer.Flag.String("master.peers", "", "all master nodes in comma separated ip:masterPort list")
	masterOptions.volumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.")
	masterOptions.volumePreallocate = cmdServer.Flag.Bool("master.volumePreallocate", false, "Preallocate disk space for volumes.")
	masterOptions.defaultReplication = cmdServer.Flag.String("master.defaultReplication", "000", "Default replication type if not specified.")
	masterOptions.garbageThreshold = cmdServer.Flag.Float64("garbageThreshold", 0.3, "threshold to vacuum and reclaim spaces")
	masterOptions.metricsAddress = cmdServer.Flag.String("metrics.address", "", "Prometheus gateway address")
	masterOptions.metricsIntervalSec = cmdServer.Flag.Int("metrics.intervalSeconds", 15, "Prometheus push interval in seconds")
	masterOptions.raftResumeState = cmdServer.Flag.Bool("resumeState", false, "resume previous state on start master server")

	filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection")
	filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port")
	filerOptions.publicPort = cmdServer.Flag.Int("filer.port.public", 0, "filer server public http listen port")
	filerOptions.defaultReplicaPlacement = cmdServer.Flag.String("filer.defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.")
	filerOptions.disableDirListing = cmdServer.Flag.Bool("filer.disableDirListing", false, "turn off directory listing")
	filerOptions.maxMB = cmdServer.Flag.Int("filer.maxMB", 32, "split files larger than the limit")
	filerOptions.dirListingLimit = cmdServer.Flag.Int("filer.dirListLimit", 1000, "limit sub dir listing size")
	filerOptions.cipher = cmdServer.Flag.Bool("filer.encryptVolumeData", false, "encrypt data on volume servers")
	filerOptions.peers = cmdServer.Flag.String("filer.peers", "", "all filers sharing the same filer store in comma separated ip:port list")
	filerOptions.cacheToFilerLimit = cmdServer.Flag.Int("filer.cacheToFilerLimit", 0, "Small files smaller than this limit can be cached in filer store.")

	serverOptions.v.port = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port")
	serverOptions.v.publicPort = cmdServer.Flag.Int("volume.port.public", 0, "volume server public port")
	serverOptions.v.indexType = cmdServer.Flag.String("volume.index", "memory", "Choose [memory|leveldb|leveldbMedium|leveldbLarge] mode for memory~performance balance.")
	serverOptions.v.fixJpgOrientation = cmdServer.Flag.Bool("volume.images.fix.orientation", false, "Adjust jpg orientation when uploading.")
	serverOptions.v.readRedirect = cmdServer.Flag.Bool("volume.read.redirect", true, "Redirect moved or non-local volumes.")
	serverOptions.v.compactionMBPerSecond = cmdServer.Flag.Int("volume.compactionMBps", 0, "limit compaction speed in mega bytes per second")
	serverOptions.v.fileSizeLimitMB = cmdServer.Flag.Int("volume.fileSizeLimitMB", 256, "limit file size to avoid out of memory")
	serverOptions.v.publicUrl = cmdServer.Flag.String("volume.publicUrl", "", "publicly accessible address")
	serverOptions.v.preStopSeconds = cmdServer.Flag.Int("volume.preStopSeconds", 10, "number of seconds between stop send heartbeats and stop volume server")
	serverOptions.v.pprof = cmdServer.Flag.Bool("volume.pprof", false, "enable pprof http handlers. precludes --memprofile and --cpuprofile")
	serverOptions.v.idxFolder = cmdServer.Flag.String("volume.dir.idx", "", "directory to store .idx files")

	s3Options.port = cmdServer.Flag.Int("s3.port", 8333, "s3 server http listen port")
	s3Options.domainName = cmdServer.Flag.String("s3.domainName", "", "suffix of the host name in comma separated list, {bucket}.{domainName}")
	s3Options.tlsPrivateKey = cmdServer.Flag.String("s3.key.file", "", "path to the TLS private key file")
	s3Options.tlsCertificate = cmdServer.Flag.String("s3.cert.file", "", "path to the TLS certificate file")
	s3Options.config = cmdServer.Flag.String("s3.config", "", "path to the config file")
	s3Options.allowEmptyFolder = cmdServer.Flag.Bool("s3.allowEmptyFolder", false, "allow empty folders")

	msgBrokerOptions.port = cmdServer.Flag.Int("msgBroker.port", 17777, "broker gRPC listen port")

}

func runServer(cmd *Command, args []string) bool {

	util.LoadConfiguration("security", false)
	util.LoadConfiguration("master", false)

	if *serverOptions.cpuprofile != "" {
		f, err := os.Create(*serverOptions.cpuprofile)
		if err != nil {
			glog.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *isStartingS3 {
		*isStartingFiler = true
	}
	if *isStartingMsgBroker {
		*isStartingFiler = true
	}

	_, peerList := checkPeers(*serverIp, *masterOptions.port, *masterOptions.peers)
	peers := strings.Join(peerList, ",")
	masterOptions.peers = &peers

	// ip address
	masterOptions.ip = serverIp
	masterOptions.ipBind = serverBindIp
	filerOptions.masters = &peers
	filerOptions.ip = serverIp
	filerOptions.bindIp = serverBindIp
	serverOptions.v.ip = serverIp
	serverOptions.v.bindIp = serverBindIp
	serverOptions.v.masters = &peers
	serverOptions.v.idleConnectionTimeout = serverTimeout
	serverOptions.v.dataCenter = serverDataCenter
	serverOptions.v.rack = serverRack
	msgBrokerOptions.ip = serverIp

	// serverOptions.v.pulseSeconds = pulseSeconds
	// masterOptions.pulseSeconds = pulseSeconds

	masterOptions.whiteList = serverWhiteListOption

	filerOptions.dataCenter = serverDataCenter
	filerOptions.rack = serverRack
	filerOptions.disableHttp = serverDisableHttp
	masterOptions.disableHttp = serverDisableHttp

	filerAddress := fmt.Sprintf("%s:%d", *serverIp, *filerOptions.port)
	s3Options.filer = &filerAddress
	msgBrokerOptions.filer = &filerAddress

	runtime.GOMAXPROCS(runtime.NumCPU())
	go stats_collect.StartMetricsServer(*serverMetricsHttpPort)

	folders := strings.Split(*volumeDataFolders, ",")

	if *masterOptions.volumeSizeLimitMB > util.VolumeSizeLimitGB*1000 {
		glog.Fatalf("masterVolumeSizeLimitMB should be less than 30000")
	}

	if *masterOptions.metaFolder == "" {
		*masterOptions.metaFolder = folders[0]
	}
	if err := util.TestFolderWritable(util.ResolvePath(*masterOptions.metaFolder)); err != nil {
		glog.Fatalf("Check Meta Folder (-mdir=\"%s\") Writable: %s", *masterOptions.metaFolder, err)
	}
	filerOptions.defaultLevelDbDirectory = masterOptions.metaFolder

	if *serverWhiteListOption != "" {
		serverWhiteList = strings.Split(*serverWhiteListOption, ",")
	}

	if *isStartingFiler {
		go func() {
			time.Sleep(1 * time.Second)

			filerOptions.startFiler()

		}()
	}

	if *isStartingS3 {
		go func() {
			time.Sleep(2 * time.Second)

			s3Options.startS3Server()

		}()
	}

	if *isStartingMsgBroker {
		go func() {
			time.Sleep(2 * time.Second)
			msgBrokerOptions.startQueueServer()
		}()
	}

	// start volume server
	if *isStartingVolumeServer {
		go serverOptions.v.startVolumeServer(*volumeDataFolders, *volumeMaxDataVolumeCounts, *serverWhiteListOption, *volumeMinFreeSpacePercent)

	}

	startMaster(masterOptions, serverWhiteList)

	return true
}
<commit_msg>server: add option to disable master<commit_after>package command

import (
	"fmt"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"time"

	stats_collect "github.com/chrislusf/seaweedfs/weed/stats"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/util"
)

type ServerOptions struct {
	cpuprofile *string
	v          VolumeServerOptions
}

var (
	serverOptions    ServerOptions
	masterOptions    MasterOptions
	filerOptions     FilerOptions
	s3Options        S3Options
	msgBrokerOptions MessageBrokerOptions
)

func init() {
	cmdServer.Run = runServer // break init cycle
}

var cmdServer = &Command{
	UsageLine: "server -dir=/tmp -volume.max=5 -ip=server_name",
	Short:     "start a master server, a volume server, and optionally a filer and a S3 gateway",
	Long: `start both a volume server to provide storage spaces
  and a master server to provide volume=>location mapping service and sequence number of file ids

  This is provided as a convenient way to start both volume server and master server.
  The servers acts exactly the same as starting them separately.
  So other volume servers can connect to this master server also.

  Optionally, a filer server can be started.
  Also optionally, a S3 gateway can be started.

  `,
}

var (
	serverIp                  = cmdServer.Flag.String("ip", util.DetectedHostAddress(), "ip or server name")
	serverBindIp              = cmdServer.Flag.String("ip.bind", "0.0.0.0", "ip address to bind to")
	serverTimeout             = cmdServer.Flag.Int("idleTimeout", 30, "connection idle seconds")
	serverDataCenter          = cmdServer.Flag.String("dataCenter", "", "current volume server's data center name")
	serverRack                = cmdServer.Flag.String("rack", "", "current volume server's rack name")
	serverWhiteListOption     = cmdServer.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")
	serverDisableHttp         = cmdServer.Flag.Bool("disableHttp", false, "disable http requests, only gRPC operations are allowed.")
	volumeDataFolders         = cmdServer.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
	volumeMaxDataVolumeCounts = cmdServer.Flag.String("volume.max", "8", "maximum numbers of volumes, count[,count]... If set to zero, the limit will be auto configured.")
	volumeMinFreeSpacePercent = cmdServer.Flag.String("volume.minFreeSpacePercent", "1", "minimum free disk space (default to 1%). Low disk space will mark all volumes as ReadOnly.")
	serverMetricsHttpPort     = cmdServer.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")

	// pulseSeconds              = cmdServer.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
	isStartingMasterServer = cmdServer.Flag.Bool("master", true, "whether to start master server")
	isStartingVolumeServer = cmdServer.Flag.Bool("volume", true, "whether to start volume server")
	isStartingFiler        = cmdServer.Flag.Bool("filer", false, "whether to start filer")
	isStartingS3           = cmdServer.Flag.Bool("s3", false, "whether to start S3 gateway")
	isStartingMsgBroker    = cmdServer.Flag.Bool("msgBroker", false, "whether to start message broker")

	serverWhiteList []string

	False = false
)

func init() {
	serverOptions.cpuprofile = cmdServer.Flag.String("cpuprofile", "", "cpu profile output file")

	masterOptions.port = cmdServer.Flag.Int("master.port", 9333, "master server http listen port")
	masterOptions.metaFolder = cmdServer.Flag.String("master.dir", "", "data directory to store meta data, default to same as -dir specified")
	masterOptions.peers = cmdServer.Flag.String("master.peers", "", "all master nodes in comma separated ip:masterPort list")
	masterOptions.volumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.")
	masterOptions.volumePreallocate = cmdServer.Flag.Bool("master.volumePreallocate", false, "Preallocate disk space for volumes.")
	masterOptions.defaultReplication = cmdServer.Flag.String("master.defaultReplication", "000", "Default replication type if not specified.")
	masterOptions.garbageThreshold = cmdServer.Flag.Float64("garbageThreshold", 0.3, "threshold to vacuum and reclaim spaces")
	masterOptions.metricsAddress = cmdServer.Flag.String("metrics.address", "", "Prometheus gateway address")
	masterOptions.metricsIntervalSec = cmdServer.Flag.Int("metrics.intervalSeconds", 15, "Prometheus push interval in seconds")
	masterOptions.raftResumeState = cmdServer.Flag.Bool("resumeState", false, "resume previous state on start master server")

	filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection")
	filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port")
	filerOptions.publicPort = cmdServer.Flag.Int("filer.port.public", 0, "filer server public http listen port")
	filerOptions.defaultReplicaPlacement = cmdServer.Flag.String("filer.defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.")
	filerOptions.disableDirListing = cmdServer.Flag.Bool("filer.disableDirListing", false, "turn off directory listing")
	filerOptions.maxMB = cmdServer.Flag.Int("filer.maxMB", 32, "split files larger than the limit")
	filerOptions.dirListingLimit = cmdServer.Flag.Int("filer.dirListLimit", 1000, "limit sub dir listing size")
	filerOptions.cipher = cmdServer.Flag.Bool("filer.encryptVolumeData", false, "encrypt data on volume servers")
	filerOptions.peers = cmdServer.Flag.String("filer.peers", "", "all filers sharing the same filer store in comma separated ip:port list")
	filerOptions.cacheToFilerLimit = cmdServer.Flag.Int("filer.cacheToFilerLimit", 0, "Small files smaller than this limit can be cached in filer store.")

	serverOptions.v.port = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port")
	serverOptions.v.publicPort = cmdServer.Flag.Int("volume.port.public", 0, "volume server public port")
	serverOptions.v.indexType = cmdServer.Flag.String("volume.index", "memory", "Choose [memory|leveldb|leveldbMedium|leveldbLarge] mode for memory~performance balance.")
	serverOptions.v.fixJpgOrientation = cmdServer.Flag.Bool("volume.images.fix.orientation", false, "Adjust jpg orientation when uploading.")
	serverOptions.v.readRedirect = cmdServer.Flag.Bool("volume.read.redirect", true, "Redirect moved or non-local volumes.")
	serverOptions.v.compactionMBPerSecond = cmdServer.Flag.Int("volume.compactionMBps", 0, "limit compaction speed in mega bytes per second")
	serverOptions.v.fileSizeLimitMB = cmdServer.Flag.Int("volume.fileSizeLimitMB", 256, "limit file size to avoid out of memory")
	serverOptions.v.publicUrl = cmdServer.Flag.String("volume.publicUrl", "", "publicly accessible address")
	serverOptions.v.preStopSeconds = cmdServer.Flag.Int("volume.preStopSeconds", 10, "number of seconds between stop send heartbeats and stop volume server")
	serverOptions.v.pprof = cmdServer.Flag.Bool("volume.pprof", false, "enable pprof http handlers. precludes --memprofile and --cpuprofile")
	serverOptions.v.idxFolder = cmdServer.Flag.String("volume.dir.idx", "", "directory to store .idx files")

	s3Options.port = cmdServer.Flag.Int("s3.port", 8333, "s3 server http listen port")
	s3Options.domainName = cmdServer.Flag.String("s3.domainName", "", "suffix of the host name in comma separated list, {bucket}.{domainName}")
	s3Options.tlsPrivateKey = cmdServer.Flag.String("s3.key.file", "", "path to the TLS private key file")
	s3Options.tlsCertificate = cmdServer.Flag.String("s3.cert.file", "", "path to the TLS certificate file")
	s3Options.config = cmdServer.Flag.String("s3.config", "", "path to the config file")
	s3Options.allowEmptyFolder = cmdServer.Flag.Bool("s3.allowEmptyFolder", false, "allow empty folders")

	msgBrokerOptions.port = cmdServer.Flag.Int("msgBroker.port", 17777, "broker gRPC listen port")

}

func runServer(cmd *Command, args []string) bool {

	util.LoadConfiguration("security", false)
	util.LoadConfiguration("master", false)

	if *serverOptions.cpuprofile != "" {
		f, err := os.Create(*serverOptions.cpuprofile)
		if err != nil {
			glog.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *isStartingS3 {
		*isStartingFiler = true
	}
	if *isStartingMsgBroker {
		*isStartingFiler = true
	}

	_, peerList := checkPeers(*serverIp, *masterOptions.port, *masterOptions.peers)
	peers := strings.Join(peerList, ",")
	masterOptions.peers = &peers

	// ip address
	masterOptions.ip = serverIp
	masterOptions.ipBind = serverBindIp
	filerOptions.masters = &peers
	filerOptions.ip = serverIp
	filerOptions.bindIp = serverBindIp
	serverOptions.v.ip = serverIp
	serverOptions.v.bindIp = serverBindIp
	serverOptions.v.masters = &peers
	serverOptions.v.idleConnectionTimeout = serverTimeout
	serverOptions.v.dataCenter = serverDataCenter
	serverOptions.v.rack = serverRack
	msgBrokerOptions.ip = serverIp

	// serverOptions.v.pulseSeconds = pulseSeconds
	// masterOptions.pulseSeconds = pulseSeconds

	masterOptions.whiteList = serverWhiteListOption

	filerOptions.dataCenter = serverDataCenter
	filerOptions.rack = serverRack
	filerOptions.disableHttp = serverDisableHttp
	masterOptions.disableHttp = serverDisableHttp

	filerAddress := fmt.Sprintf("%s:%d", *serverIp, *filerOptions.port)
	s3Options.filer = &filerAddress
	msgBrokerOptions.filer = &filerAddress

	runtime.GOMAXPROCS(runtime.NumCPU())
	go stats_collect.StartMetricsServer(*serverMetricsHttpPort)

	folders := strings.Split(*volumeDataFolders, ",")

	if *masterOptions.volumeSizeLimitMB > util.VolumeSizeLimitGB*1000 {
		glog.Fatalf("masterVolumeSizeLimitMB should be less than 30000")
	}

	if *masterOptions.metaFolder == "" {
		*masterOptions.metaFolder = folders[0]
	}
	if err := util.TestFolderWritable(util.ResolvePath(*masterOptions.metaFolder)); err != nil {
		glog.Fatalf("Check Meta Folder (-mdir=\"%s\") Writable: %s", *masterOptions.metaFolder, err)
	}
	filerOptions.defaultLevelDbDirectory = masterOptions.metaFolder

	if *serverWhiteListOption != "" {
		serverWhiteList = strings.Split(*serverWhiteListOption, ",")
	}

	if *isStartingFiler {
		go func() {
			time.Sleep(1 * time.Second)

			filerOptions.startFiler()

		}()
	}

	if *isStartingS3 {
		go func() {
			time.Sleep(2 * time.Second)

			s3Options.startS3Server()

		}()
	}

	if *isStartingMsgBroker {
		go func() {
			time.Sleep(2 * time.Second)
			msgBrokerOptions.startQueueServer()
		}()
	}

	// start volume server
	if *isStartingVolumeServer {
		go serverOptions.v.startVolumeServer(*volumeDataFolders, *volumeMaxDataVolumeCounts, *serverWhiteListOption, *volumeMinFreeSpacePercent)

	}

	if *isStartingMasterServer {
		go startMaster(masterOptions, serverWhiteList)
	}

	select {}

	return true
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"os"
)

var (
	configFile = "leedsgeeks.json"
	port       = flag.String("port", "5000", "http port")
	templates  = template.Must(template.ParseGlob("templates/*.html"))
)

type Config struct {
	Maintainer    string
	RepositoryURL string
	Groups        []Group
}

type Group struct {
	Name        string
	Description string
	Schedule    string
	Links       []Link
}

type Link struct {
	Label    string
	URL      string
	LinkText string
}

func main() {
	flag.Parse()
	http.HandleFunc("/", index)
	http.Handle("/_/", http.StripPrefix("/_/", http.FileServer(http.Dir("./static/"))))
	http.ListenAndServe(":"+*port, nil)
}

func index(w http.ResponseWriter, r *http.Request) {

	config, err := readConfig()
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	err = templates.ExecuteTemplate(w, "index.html", config)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
}

func readConfig() (*Config, error) {

	f, err := os.Open(configFile)
	if err != nil {
		return nil, fmt.Errorf("%s not found", configFile)
	}
	defer f.Close()

	config := &Config{}
	err = json.NewDecoder(f).Decode(config)
	if err != nil {
		return nil, fmt.Errorf("%s is invalid", configFile)
	}

	return config, nil
}
<commit_msg>Move the port flag to main().<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"os"
)

var (
	configFile = "leedsgeeks.json"
	templates  = template.Must(template.ParseGlob("templates/*.html"))
)

type Config struct {
	Maintainer    string
	RepositoryURL string
	Groups        []Group
}

type Group struct {
	Name        string
	Description string
	Schedule    string
	Links       []Link
}

type Link struct {
	Label    string
	URL      string
	LinkText string
}

func main() {
	port := flag.String("port", "5000", "http port")
	flag.Parse()

	http.HandleFunc("/", index)
	http.Handle("/_/", http.StripPrefix("/_/", http.FileServer(http.Dir("./static/"))))
	http.ListenAndServe(":"+*port, nil)
}

func index(w http.ResponseWriter, r *http.Request) {

	config, err := readConfig()
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	err = templates.ExecuteTemplate(w, "index.html", config)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
}

func readConfig() (*Config, error) {

	f, err := os.Open(configFile)
	if err != nil {
		return nil, fmt.Errorf("%s not found", configFile)
	}
	defer f.Close()

	config := &Config{}
	err = json.NewDecoder(f).Decode(config)
	if err != nil {
		return nil, fmt.Errorf("%s is invalid", configFile)
	}

	return config, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/format"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
	"strings"
	"text/template"
	"golang.org/x/tools/imports"
)

type Type struct {
	Name       string              `json:"name"`
	Base       string              `json:"base"`
	TypeDoc    string              `json:"typeDoc"`
	Properties map[string]Property `json:"properties"`
	ServiceDoc string              `json:"serviceDoc"`
	Methods    map[string]Method   `json:"methods"`
	NoService  bool                `json:"noservice"`
}

type Property struct {
	Name      string `json:"name"`
	Type      string `json:"type"`
	TypeArray bool   `json:"typeArray"`
	Form      string `json:"form"`
	Doc       string `json:"doc"`
}

type Method struct {
	Name       string      `json:"name"`
	Type       string      `json:"type"`
	TypeArray  bool        `json:"typeArray"`
	Doc        string      `json:"doc"`
	Static     bool        `json:"static"`
	NoAuth     bool        `json:"noauth"`
	Limitable  bool        `json:"limitable"`
	Filterable bool        `json:"filterable"`
	Maskable   bool        `json:"maskable"`
	Parameters []Parameter `json:"parameters"`
}

type Parameter struct {
	Name         string      `json:"name"`
	Type         string      `json:"type"`
	TypeArray    bool        `json:"typeArray"`
	Doc          string      `json:"doc"`
	DefaultValue interface{} `json:"defaultValue"`
}

// Define custom template functions
var fMap = template.FuncMap{
	"convertType":       ConvertType,           // Converts SoftLayer types to Go types
	"prefixWithPackage": PrefixWithPackageName, // Prepend a type with the given package name
	"removePrefix":      RemovePrefix,          // Remove 'SoftLayer_' prefix. if it exists
	"removeReserved":    RemoveReservedWords,   // Substitute language-reserved identifiers
	"titleCase":         strings.Title,         // TitleCase the argument
}

const datatype = `package datatypes

{{range .}}type {{.Name|removePrefix}} struct {
    {{.Base|removePrefix}}

    {{range .Properties}}{{.Name|titleCase}} {{if .TypeArray}}[]{{else}}*{{end}}{{.Type|convertType|removePrefix}}` +
	"`json:\"{{.Name}}:omitempty\"`" + `
    {{end}}
}

{{end}}
`

const iface = `package softlayer

{{range .}}type {{.Name|removePrefix}} interface {
    {{.Base|removePrefix}}

    {{range .Methods}}{{.Name|titleCase}}({{range .Parameters}}{{.Name|removeReserved}} {{.Type|convertType|prefixWithPackage "datatypes"}}, {{end}}) ({{if .Type|ne "void"}}{{.Type|convertType|prefixWithPackage "datatypes"}}, {{end}}error)
    {{end}}
}

{{end}}
`

func main() {
	var meta map[string]Type

	outputPath := flag.String("o", ".", "the root of the go project to be refreshed")
	flag.Parse()

	jsonResp, code, err := makeHttpRequest("https://api.softlayer.com/metadata/v3.1", "GET", new(bytes.Buffer))

	if err != nil {
		fmt.Printf("Error retrieving metadata API: %s", err)
		os.Exit(1)
	}

	if code != 200 {
		fmt.Printf("Unexpected HTTP status code received while retrieving metadata API: %d", code)
		os.Exit(1)
	}

	err = json.Unmarshal(jsonResp, &meta)
	if err != nil {
		fmt.Printf("Error unmarshaling json response: %s", err)
		os.Exit(1)
	}

	err = clearTargetDirs(*outputPath, []string{"datatypes", "softlayer", "services"})
	if err != nil {
		fmt.Printf("Error preparing target directories: %s", err)
		os.Exit(1)
	}

	// Build an array of Types, sorted by name
	// This will ensure consistency in the order that code is later emitted
	keys := getSortedKeys(meta)

	sortedMeta := make([]Type, 0, len(keys))

	for _, name := range keys {
		sortedMeta = append(sortedMeta, meta[name])
	}

	err = writeGoFile(*outputPath, "datatypes", "sl_datatypes", sortedMeta, datatype)
	if err != nil {
		fmt.Printf("Error writing to file: %s", err)
	}

	err = writeGoFile(*outputPath, "softlayer", "sl_interfaces", sortedMeta, iface)
	if err != nil {
		fmt.Printf("Error writing to file: %s", err)
	}
}

// Exported template functions

func RemovePrefix(args ...interface{}) string {
	s := args[0].(string)

	if strings.HasPrefix(s, "SoftLayer_") {
		return s[10:]
	}

	return s
}

func PrefixWithPackageName(args ...interface{}) string {
	p := args[0].(string)
	s := args[1].(string)

	if !strings.HasPrefix(s, "SoftLayer_") {
		return s
	}

	return p + "." + RemovePrefix(s)
}

func ConvertType(args ...interface{}) string {
	t := args[0].(string)

	// Convert softlayer types to golang types
	switch t {
	case "unsignedLong", "unsignedInt":
		return "uint"
	case "boolean":
		return "bool"
	case "dateTime":
		return "time.Time"
	case "decimal", "float":
		return "float64"
	case "base64Binary":
		return "[]byte"
	case "json", "enum":
		return "string"
	}

	return t
}

func RemoveReservedWords(args ...interface{}) string {
	n := args[0].(string)

	// Replace language reserved identifiers with alternatives
	switch n {
	case "type":
		return "typ"
	}

	return n
}

// private

func getSortedKeys(m map[string]Type) []string {
	keys := make([]string, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	return keys
}

// Executes a template against the metadata structure, and generates a go source file with the result
func writeGoFile(base string, pkg string, name string, meta []Type, ts string) error {
	filename := base + "/" + pkg + "/" + name + ".go"

	// Generate the source
	var buf bytes.Buffer
	t := template.New(pkg).Funcs(fMap)
	template.Must(t.Parse(ts)).Execute(&buf, meta)

	// Add the imports
	src, err := imports.Process(filename, buf.Bytes(), &imports.Options{})

	// Format
	pretty, err := format.Source(src)
	if err != nil {
		panic(err)
	}

	f, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Error creating file: %s", err)
	}
	defer f.Close()
	fmt.Fprintf(f, "%s", pretty)

	return nil
}

func clearTargetDirs(base string, dirs []string) error {
	for _, d := range dirs {
		err := os.RemoveAll(base + "/" + d)
		if err != nil {
			return fmt.Errorf("Unable to remove %s directory: %s", d, err)
		}

		err = os.Mkdir(base+"/"+d, 0755)
		if err != nil {
			return fmt.Errorf("Unable to create %s directory: %s", d, err)
		}
	}

	return nil
}

func makeHttpRequest(url string, requestType string, requestBody *bytes.Buffer) ([]byte, int, error) {
	client := http.DefaultClient

	req, err := http.NewRequest(requestType, url, requestBody)
	if err != nil {
		return nil, 0, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, 520, err
	}

	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, resp.StatusCode, err
	}

	return responseBody, resp.StatusCode, nil
}
<commit_msg>Return error to main<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/format"
	"io/ioutil"
	"net/http"
	"os"
	"sort"
	"strings"
	"text/template"
	"golang.org/x/tools/imports"
)

type Type struct {
	Name       string              `json:"name"`
	Base       string              `json:"base"`
	TypeDoc    string              `json:"typeDoc"`
	Properties map[string]Property `json:"properties"`
	ServiceDoc string              `json:"serviceDoc"`
	Methods    map[string]Method   `json:"methods"`
	NoService  bool                `json:"noservice"`
}

type Property struct {
	Name      string `json:"name"`
	Type      string `json:"type"`
	TypeArray bool   `json:"typeArray"`
	Form      string `json:"form"`
	Doc       string `json:"doc"`
}

type Method struct {
	Name       string      `json:"name"`
	Type       string      `json:"type"`
	TypeArray  bool        `json:"typeArray"`
	Doc        string      `json:"doc"`
	Static     bool        `json:"static"`
	NoAuth     bool        `json:"noauth"`
	Limitable  bool        `json:"limitable"`
	Filterable bool        `json:"filterable"`
	Maskable   bool        `json:"maskable"`
	Parameters []Parameter `json:"parameters"`
}

type Parameter struct {
	Name         string      `json:"name"`
	Type         string      `json:"type"`
	TypeArray    bool        `json:"typeArray"`
	Doc          string      `json:"doc"`
	DefaultValue interface{} `json:"defaultValue"`
}

// Define custom template functions
var fMap = template.FuncMap{
	"convertType":       ConvertType,           // Converts SoftLayer types to Go types
	"prefixWithPackage": PrefixWithPackageName, // Prepend a type with the given package name
	"removePrefix":      RemovePrefix,          // Remove 'SoftLayer_' prefix. if it exists
	"removeReserved":    RemoveReservedWords,   // Substitute language-reserved identifiers
	"titleCase":         strings.Title,         // TitleCase the argument
}

const datatype = `package datatypes

{{range .}}type {{.Name|removePrefix}} struct {
    {{.Base|removePrefix}}

    {{range .Properties}}{{.Name|titleCase}} {{if .TypeArray}}[]{{else}}*{{end}}{{.Type|convertType|removePrefix}}` +
	"`json:\"{{.Name}}:omitempty\"`" + `
    {{end}}
}

{{end}}
`

const iface = `package softlayer

{{range .}}type {{.Name|removePrefix}} interface {
    {{.Base|removePrefix}}

    {{range .Methods}}{{.Name|titleCase}}({{range .Parameters}}{{.Name|removeReserved}} {{.Type|convertType|prefixWithPackage "datatypes"}}, {{end}}) ({{if .Type|ne "void"}}{{.Type|convertType|prefixWithPackage "datatypes"}}, {{end}}error)
    {{end}}
}

{{end}}
`

func main() {
	var meta map[string]Type

	outputPath := flag.String("o", ".", "the root of the go project to be refreshed")
	flag.Parse()

	jsonResp, code, err := makeHttpRequest("https://api.softlayer.com/metadata/v3.1", "GET", new(bytes.Buffer))

	if err != nil {
		fmt.Printf("Error retrieving metadata API: %s", err)
		os.Exit(1)
	}

	if code != 200 {
		fmt.Printf("Unexpected HTTP status code received while retrieving metadata API: %d", code)
		os.Exit(1)
	}

	err = json.Unmarshal(jsonResp, &meta)
	if err != nil {
		fmt.Printf("Error unmarshaling json response: %s", err)
		os.Exit(1)
	}

	err = clearTargetDirs(*outputPath, []string{"datatypes", "softlayer", "services"})
	if err != nil {
		fmt.Printf("Error preparing target directories: %s", err)
		os.Exit(1)
	}

	// Build an array of Types, sorted by name
	// This will ensure consistency in the order that code is later emitted
	keys := getSortedKeys(meta)

	sortedMeta := make([]Type, 0, len(keys))

	for _, name := range keys {
		sortedMeta = append(sortedMeta, meta[name])
	}

	err = writeGoFile(*outputPath, "datatypes", "sl_datatypes", sortedMeta, datatype)
	if err != nil {
		fmt.Printf("Error writing to file: %s", err)
	}

	err = writeGoFile(*outputPath, "softlayer", "sl_interfaces", sortedMeta, iface)
	if err != nil {
		fmt.Printf("Error writing to file: %s", err)
	}
}

// Exported template functions

func RemovePrefix(args ...interface{}) string {
	s := args[0].(string)

	if strings.HasPrefix(s, "SoftLayer_") {
		return s[10:]
	}

	return s
}

func PrefixWithPackageName(args ...interface{}) string {
	p := args[0].(string)
	s := args[1].(string)

	if !strings.HasPrefix(s, "SoftLayer_") {
		return s
	}

	return p + "." + RemovePrefix(s)
}

func ConvertType(args ...interface{}) string {
	t := args[0].(string)

	// Convert softlayer types to golang types
	switch t {
	case "unsignedLong", "unsignedInt":
		return "uint"
	case "boolean":
		return "bool"
	case "dateTime":
		return "time.Time"
	case "decimal", "float":
		return "float64"
	case "base64Binary":
		return "[]byte"
	case "json", "enum":
		return "string"
	}

	return t
}

func RemoveReservedWords(args ...interface{}) string {
	n := args[0].(string)

	// Replace language reserved identifiers with alternatives
	switch n {
	case "type":
		return "typ"
	}

	return n
}

// private

func getSortedKeys(m map[string]Type) []string {
	keys := make([]string, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	return keys
}

// Executes a template against the metadata structure, and generates a go source file with the result
func writeGoFile(base string, pkg string, name string, meta []Type, ts string) error {
	filename := base + "/" + pkg + "/" + name + ".go"

	// Generate the source
	var buf bytes.Buffer
	t := template.New(pkg).Funcs(fMap)
	template.Must(t.Parse(ts)).Execute(&buf, meta)

	// Add the imports
	src, err := imports.Process(filename, buf.Bytes(), &imports.Options{})

	// Format
	pretty, err := format.Source(src)
	if err != nil {
		return fmt.Errorf("Error while formatting source: %s", err)
	}

	f, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Error creating file: %s", err)
	}
	defer f.Close()
	fmt.Fprintf(f, "%s", pretty)

	return nil
}

func clearTargetDirs(base string, dirs []string) error {
	for _, d := range dirs {
		err := os.RemoveAll(base + "/" + d)
		if err != nil {
			return fmt.Errorf("Unable to remove %s directory: %s", d, err)
		}

		err = os.Mkdir(base+"/"+d, 0755)
		if err != nil {
			return fmt.Errorf("Unable to create %s directory: %s", d, err)
		}
	}

	return nil
}

func makeHttpRequest(url string, requestType string, requestBody *bytes.Buffer) ([]byte, int, error) {
	client := http.DefaultClient

	req, err := http.NewRequest(requestType, url, requestBody)
	if err != nil {
		return nil, 0, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, 520, err
	}

	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, resp.StatusCode, err
	}

	return responseBody, resp.StatusCode, nil
}
<|endoftext|>


<commit_before>package mark

import (
	"fmt"
	"testing"
)

var itemName = map[itemType]string{
	itemError:        "Error",
	itemEOF:          "EOF",
	itemNewLine:      "NewLine",
	itemHTML:         "HTML",
	itemHeading:      "Heading",
	itemLHeading:     "LHeading",
	itemBlockQuote:   "BlockQuote",
	itemList:         "List",
	itemListItem:     "ListItem",
	itemLooseItem:    "LooseItem",
	itemCodeBlock:    "CodeBlock",
	itemGfmCodeBlock: "GfmCodeBlock",
	itemHr:           "Hr",
	itemTable:        "Table",
	itemLpTable:      "LpTable",
	itemText:         "Text",
	itemLink:         "Link",
	itemDefLink:      "DefLink",
	itemRefLink:      "RefLink",
	itemAutoLink:     "AutoLink",
	itemGfmLink:      "GfmLink",
	itemStrong:       "Strong",
	itemItalic:       "Italic",
	itemStrike:       "Strike",
	itemCode:         "Code",
	itemImage:        "Image",
	itemRefImage:     "RefImage",
	itemBr:           "Br",
	itemPipe:         "Pipe",
	itemIndent:       "Indent",
}

func (i itemType) String() string {
	s := itemName[i]
	if s == "" {
		return fmt.Sprintf("item%d", int(i))
	}
	return s
}

type lexTest struct {
	name  string
	input string
	items []item
}

var (
	tEOF = item{itemEOF, 0, ""}
)

var lexTests = []lexTest{
	{"empty", "", []item{tEOF}},
	{"heading", "# Hello", []item{
		{itemHeading, 0, "# Hello"},
		tEOF,
	}},
	{"lheading", "Hello\n===", []item{
		{itemLHeading, 0, "Hello\n==="},
		tEOF,
	}},
	{"blockqoute", "> foo bar", []item{
		{itemBlockQuote, 0, "> foo bar"},
		tEOF,
	}},
	{"unordered list", "- foo\n- bar", []item{
		{itemList, 0, "-"},
		{itemListItem, 0, "foo"},
		{itemListItem, 0, "bar"},
		tEOF,
	}},
	{"ordered list", "1. foo\n2. bar", []item{
		{itemList, 0, "1."},
		{itemListItem, 0, "foo"},
		{itemListItem, 0, "bar"},
		tEOF,
	}},
	{"loose-items", "- foo\n\n- bar", []item{
		{itemList, 0, "-"},
		{itemLooseItem, 0, "foo"},
		{itemLooseItem, 0, "bar"},
		tEOF,
	}},
}

// collect gathers the emitted items into a slice.
func collect(t *lexTest) (items []item) {
	l := lex(t.input)
	for {
		item := l.nextItem()
		items = append(items, item)
		if item.typ == itemEOF || item.typ == itemError {
			break
		}
	}
	return
}

func equal(i1, i2 []item, checkPos bool) bool {
	if len(i1) != len(i2) {
		return false
	}
	for k := range i1 {
		if i1[k].typ != i2[k].typ {
			return false
		}
		if i1[k].val != i2[k].val {
			return false
		}
		if checkPos && i1[k].pos != i2[k].pos {
			return false
		}
	}
	return true
}

func TestLex(t *testing.T) {
	for _, test := range lexTests {
		items := collect(&test)
		if !equal(items, test.items, false) {
			t.Errorf("%s: got\n\t%+v\nexpected\n\t%+v", test.name, items, test.items)
		}
	}
}
<commit_msg>test(lexer): gfm/codeblock lexing<commit_after>package mark

import (
	"fmt"
	"testing"
)

var itemName = map[itemType]string{
	itemError:        "Error",
	itemEOF:          "EOF",
	itemNewLine:      "NewLine",
	itemHTML:         "HTML",
	itemHeading:      "Heading",
	itemLHeading:     "LHeading",
	itemBlockQuote:   "BlockQuote",
	itemList:         "List",
	itemListItem:     "ListItem",
	itemLooseItem:    "LooseItem",
	itemCodeBlock:    "CodeBlock",
	itemGfmCodeBlock: "GfmCodeBlock",
	itemHr:           "Hr",
	itemTable:        "Table",
	itemLpTable:      "LpTable",
	itemText:         "Text",
	itemLink:         "Link",
	itemDefLink:      "DefLink",
	itemRefLink:      "RefLink",
	itemAutoLink:     "AutoLink",
	itemGfmLink:      "GfmLink",
	itemStrong:       "Strong",
	itemItalic:       "Italic",
	itemStrike:       "Strike",
	itemCode:         "Code",
	itemImage:        "Image",
	itemRefImage:     "RefImage",
	itemBr:           "Br",
	itemPipe:         "Pipe",
	itemIndent:       "Indent",
}

func (i itemType) String() string {
	s := itemName[i]
	if s == "" {
		return fmt.Sprintf("item%d", int(i))
	}
	return s
}

type lexTest struct {
	name  string
	input string
	items []item
}

var (
	tEOF = item{itemEOF, 0, ""}
)

var lexTests = []lexTest{
	{"empty", "", []item{tEOF}},
	{"heading", "# Hello", []item{
		{itemHeading, 0, "# Hello"},
		tEOF,
	}},
	{"lheading", "Hello\n===", []item{
		{itemLHeading, 0, "Hello\n==="},
		tEOF,
	}},
	{"blockqoute", "> foo bar", []item{
		{itemBlockQuote, 0, "> foo bar"},
		tEOF,
	}},
	{"unordered list", "- foo\n- bar", []item{
		{itemList, 0, "-"},
		{itemListItem, 0, "foo"},
		{itemListItem, 0, "bar"},
		tEOF,
	}},
	{"ordered list", "1. foo\n2. bar", []item{
		{itemList, 0, "1."},
		{itemListItem, 0, "foo"},
		{itemListItem, 0, "bar"},
		tEOF,
	}},
	{"loose-items", "- foo\n\n- bar", []item{
		{itemList, 0, "-"},
		{itemLooseItem, 0, "foo"},
		{itemLooseItem, 0, "bar"},
		tEOF,
	}},
	{"code-block", "    foo\n    bar", []item{
		{itemCodeBlock, 0, "    foo\n    bar"},
		tEOF,
	}},
	{"gfm-code-block", "~~~js\nfoo\n~~~", []item{
		{itemGfmCodeBlock, 0, "~~~js\nfoo\n~~~"},
		tEOF,
	}},
}

// collect gathers the emitted items into a slice.
func collect(t *lexTest) (items []item) {
	l := lex(t.input)
	for {
		item := l.nextItem()
		items = append(items, item)
		if item.typ == itemEOF || item.typ == itemError {
			break
		}
	}
	return
}

func equal(i1, i2 []item, checkPos bool) bool {
	if len(i1) != len(i2) {
		return false
	}
	for k := range i1 {
		if i1[k].typ != i2[k].typ {
			return false
		}
		if i1[k].val != i2[k].val {
			return false
		}
		if checkPos && i1[k].pos != i2[k].pos {
			return false
		}
	}
	return true
}

func TestLex(t *testing.T) {
	for _, test := range lexTests {
		items := collect(&test)
		if !equal(items, test.items, false) {
			t.Errorf("%s: got\n\t%+v\nexpected\n\t%+v", test.name, items, test.items)
		}
	}
}
<|endoftext|>


<commit_before>// @author Couchbase <info@couchbase.com>
// @copyright 2014 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 metakv provides simple KV API to some "metadata store". In
// current implementation it is metadata store that keeps values in
// ns_server's ns_config.
//
// Sub-namespace that is under directory "/checkpoints/" is going to
// be implemented specially. It is intended for xdcr checkpoints and
// will not be replicated between nodes in first version of this
// package.
package metakv

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/couchbase/cbauth"
)

// ErrRevMismatch error is returned from Set and Delete when there is
// rev mismatch
var ErrRevMismatch = errors.New("Rev mismatch")

var errNotFound = errors.New("Not found")

// KVEntry struct represents kv entry returned from ListAllChildren and
// used as a parameter in CallbackV2
type KVEntry struct {
	Path      string
	Value     []byte
	Rev       interface{}
	Sensitive bool
}

// Callback type describes functions that receive mutations from
// RunObserveChildren.
type Callback func(path string, value []byte, rev interface{}) error

// CallbackV2 type describes functions that receive mutations from
// RunObserveChildrenV2.
type CallbackV2 func(entry KVEntry) error

// RevCreate is a special revision which when passed to Set will change
// Set to Add.
var RevCreate = &struct{}{}

type store struct {
	url    *url.URL
	client *http.Client
}

var defaultStore = initDefaultStore()

func initDefaultStore() *store {
	c := *http.DefaultClient
	c.Transport = cbauth.WrapHTTPTransport(http.DefaultTransport, nil)

	authURL := os.Getenv("CBAUTH_REVRPC_URL")
	u, err := url.Parse(authURL)
	if err == nil {
		u.RawQuery = ""
		u.Fragment = ""
		u.Path = "/_metakv"
		u.User = nil
	}
	return &store{url: u, client: &c}
}

func doCallInner(s *store, method, path string, values url.Values) (resp *http.Response, err error) {
	var body io.Reader
	if method == "PUT" && values != nil {
		body = strings.NewReader(values.Encode())
	}
	url := *s.url
	url.Path += path
	req, err := http.NewRequest(method, url.String(), body)
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}
	if method != "PUT" && values != nil {
		req.URL.RawQuery = values.Encode()
	}
	r, err := s.client.Do(req)
	if err != nil {
		return r, err
	}
	if r.StatusCode == http.StatusConflict {
		return r, ErrRevMismatch
	}
	if r.StatusCode == http.StatusNotFound {
		return r, errNotFound
	}
	if r.StatusCode != 200 {
		return r, fmt.Errorf("ns_server _metakv returned: %s", r.Status)
	}
	return r, err
}

func doCall(s *store, method, path string, values url.Values) (body []byte, err error) {
	r, err := doCallInner(s, method, path, values)
	if r != nil {
		defer r.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(r.Body)
}

func doJSONCall(s *store, method, path string, values url.Values, place interface{}) error {
	body, err := doCall(s, method, path, values)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, place)
}

type kvEntry struct {
	Path      string
	Value     []byte
	Rev       []byte
	Sensitive bool
}

type callbackInt func(entry kvEntry) error

func assertValidPathPrefix(path string) {
	if path[0] != '/' {
		panic("path must begin with /")
	}
}

func assertValidPath(path string) {
	assertValidPathPrefix(path)
	if strings.HasSuffix(path, "/") {
		panic("path must not end with \"/\"")
	}
}

func assertValidDirPath(path string) {
	assertValidPathPrefix(path)
	if !strings.HasSuffix(path, "/") {
		panic("dirpath must end with \"/\"")
	}
}

// Get returns matching value and revision for given key. Returns nil
// value, nil rev and nil error when given path doesn't exist.
func (s *store) get(path string) (value []byte, rev interface{}, err error) {
	assertValidPath(path)
	var kve kvEntry
	err = doJSONCall(s, "GET", path, nil, &kve)
	if err == errNotFound {
		return nil, nil, nil
	}
	if err != nil {
		return nil, nil, err
	}
	rev = nil
	if kve.Rev != nil {
		rev = kve.Rev
	}
	return kve.Value, rev, nil
}

func mutate(s *store, method string, path string, value []byte, rev interface{}, create bool, sensitive bool) error {
	values := url.Values{
		"value": {string(value)},
	}

	if create || rev == RevCreate {
		rev = nil
		values.Set("create", "1")
	}

	if rev != nil {
		revBytes, ok := rev.([]byte)
		if !ok {
			return ErrRevMismatch
		}
		values.Set("rev", string(revBytes))
	}

	if sensitive {
		values.Set("sensitive", "true")
	} else {
		values.Set("sensitive", "false")
	}

	_, err := doCall(s, method, path, values)
	return err
}

// Set updates given key-value pair. If non-nil, rev is a form of CAS
// value used to detect races with concurrent mutators in typical
// read-modify-write cases. Rev is supposed to be same value that is
// returned from get.
func (s *store) set(path string, value []byte, rev interface{}, sensitive bool) error {
	assertValidPath(path)
	return mutate(s, "PUT", path, value, rev, false, sensitive)
}

// Add creates given kv pair. Which must not exist in storage
// yet. ErrRevMismatch is returned if pair with such key exists.
func (s *store) add(path string, value []byte, sensitive bool) error {
	assertValidPath(path)
	return mutate(s, "PUT", path, value, nil, true, sensitive)
}

// Delete deletes given key.
func (s *store) delete(path string, rev interface{}) error {
	assertValidPath(path)
	return mutate(s, "DELETE", path, nil, rev, false, false)
}

// Recursive Delete deletes all keys that are children of given directory path.
func (s *store) recursiveDelete(dirpath string) error {
	assertValidDirPath(dirpath)
	return mutate(s, "DELETE", dirpath, nil, nil, false, false)
}

// IterateChildren invokes given callback on every kv-pair that's
// child of given directory path. Path must end on "/".
func (s *store) iterateChildren(dirpath string, callback callbackInt) error {
	return doRunObserveChildren(s, dirpath, callback, nil)
}

// RunObserveChildren invokes gen callback on every kv-pair that is
// child of given directory path and then on every mutation of
// affected keys. Deletions will be signalled by passing nil to value
// argument of callback. Returns only when cancel channel is closed or
// when children callback returns error. If exit is due to cancel
// channel being closed returned error is nil. Otherwise error is
// non-nil. Path must end on "/".
func (s *store) runObserveChildren(dirpath string, callback callbackInt,
	cancel <-chan struct{}) error {
	if cancel == nil {
		return nil
	}
	return doRunObserveChildren(s, dirpath, callback, cancel)
}

func doRunObserveChildren(s *store, dirpath string, callback callbackInt,
	cancel <-chan struct{}) error {
	assertValidDirPath(dirpath)
	values := url.Values{}
	if cancel != nil {
		values.Set("feed", "continuous")
	}
	r, err := doCallInner(s, "GET", dirpath, values)
	if r != nil {
		defer r.Body.Close()
	}
	if err != nil {
		return err
	}

	errChan := make(chan error, 1)
	kveChan := make(chan kvEntry)

	go func() {
		dec := json.NewDecoder(r.Body)
		var kve kvEntry
		for {
			err := dec.Decode(&kve)
			if err != nil {
				errChan <- err
				close(kveChan)
				return
			}
			kveChan <- kve
		}
	}()

	effCancel := cancel
	if cancel == nil {
		effCancel = make(chan struct{})
	}

readLoop:
	for {
		select {
		case kve, ok := <-kveChan:
			if !ok {
				err = <-errChan
				break readLoop
			}
			err = callback(kve)
			if err != nil {
				return err
			}

		case err = <-errChan:
			break readLoop

		case <-effCancel:
			r.Body.Close()
			// ensure that worker goroutine is done
			for range kveChan {
			}
			return nil
		}
	}

	if cancel == nil && err == io.EOF {
		err = nil
	}
	return err
}

// Get returns matching value and revision for given key. Returns nil
// value, nil rev and nil error when given path doesn't exist.
func Get(path string) (value []byte, rev interface{}, err error) {
	return defaultStore.get(path)
}

// Set updates given key-value pair. If non-nil, rev is a form of CAS
// value used to detect races with concurrent mutators in typical
// read-modify-write cases. Rev is supposed to be same value that is
// returned from get.
func Set(path string, value []byte, rev interface{}) error {
	return defaultStore.set(path, value, rev, false)
}

// SetSensitive is Set for storing sensitive info.
func SetSensitive(path string, value []byte, rev interface{}) error {
	return defaultStore.set(path, value, rev, true)
}

// Add creates given kv pair. Which must not exist in storage
// yet. ErrRevMismatch is returned if pair with such key exists.
func Add(path string, value []byte) error {
	return defaultStore.add(path, value, false)
}

// AddSensitive is Add for storing sensitive info.
func AddSensitive(path string, value []byte) error {
	return defaultStore.add(path, value, true)
}

// Delete deletes given key.
func Delete(path string, rev interface{}) error {
	return defaultStore.delete(path, rev)
}

// RecursiveDelete deletes all keys that are children of given directory path.
func RecursiveDelete(dirpath string) error {
	return defaultStore.recursiveDelete(dirpath)
}

// IterateChildren invokes given callback on every kv-pair that's
// child of given directory path. Path must end on "/".
func IterateChildren(dirpath string, callback Callback) error {
	return defaultStore.iterateChildren(dirpath,
		func(e kvEntry) error {
			return callback(e.Path, e.Value, e.Rev)
		})
}

// RunObserveChildren invokes gen callback on every kv-pair that is
// child of given directory path and then on every mutation of
// affected keys. Deletions will be signalled by passing nil to value
// argument of callback. Returns only when cancel channel is closed or
// when children callback returns error. If exit is due to cancel
// channel being closed returned error is nil. Otherwise error is
// non-nil. Path must end on "/".
func RunObserveChildren(dirpath string, callback Callback,
	cancel <-chan struct{}) error {
	return defaultStore.runObserveChildren(dirpath,
		func(e kvEntry) error {
			return callback(e.Path, e.Value, e.Rev)
		}, cancel)
}

// IterateChildrenV2 invokes given callback on every kv-pair that's
// child of given directory path. Path must end on "/".
func IterateChildrenV2(dirpath string, callback CallbackV2) error {
	return defaultStore.iterateChildren(dirpath,
		func(e kvEntry) error {
			return callback(KVEntry{e.Path, e.Value, e.Rev,
				e.Sensitive})
		})
}

// RunObserveChildrenV2 invokes gen callback on every kv-pair that is
// child of given directory path and then on every mutation of
// affected keys. Deletions will be signalled by passing nil to value
// argument of callback. Returns only when cancel channel is closed or
// when children callback returns error. If exit is due to cancel
// channel being closed returned error is nil. Otherwise error is
// non-nil. Path must end on "/".
func RunObserveChildrenV2(dirpath string, callback CallbackV2,
	cancel <-chan struct{}) error {
	return defaultStore.runObserveChildren(dirpath,
		func(e kvEntry) error {
			return callback(KVEntry{e.Path, e.Value, e.Rev,
				e.Sensitive})
		}, cancel)
}

// ListAllChildren returns all child entries of given "directory" node.
func ListAllChildren(dirpath string) (entries []KVEntry, err error) {
	return defaultStore.listAllChildren(dirpath)
}

// ListAllChildren returns all child entries of given "directory" node.
func (s *store) listAllChildren(dirpath string) (entries []KVEntry, err error) {
	// nil could be used here, but then empty list becomes nil and
	// in my testing code it means json null is returned rather
	// than empty json array.
	entries = make([]KVEntry, 0, 16)
	err = s.iterateChildren(dirpath,
		func(e kvEntry) error {
			entries = append(entries,
				KVEntry{e.Path, e.Value, e.Rev, e.Sensitive})
			return nil
		})
	return
}
<commit_msg>make IterateChildren and RunObserveChildren identical to<commit_after>// @author Couchbase <info@couchbase.com>
// @copyright 2014 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 metakv provides simple KV API to some "metadata store". In
// current implementation it is metadata store that keeps values in
// ns_server's ns_config.
//
// Sub-namespace that is under directory "/checkpoints/" is going to
// be implemented specially. It is intended for xdcr checkpoints and
// will not be replicated between nodes in first version of this
// package.
package metakv

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/couchbase/cbauth"
)

// ErrRevMismatch error is returned from Set and Delete when there is
// rev mismatch
var ErrRevMismatch = errors.New("Rev mismatch")

var errNotFound = errors.New("Not found")

// KVEntry struct represents kv entry returned from ListAllChildren and
// used as a parameter in Callback
type KVEntry struct {
	Path      string
	Value     []byte
	Rev       interface{}
	Sensitive bool
}

// Callback type describes functions that receive mutations from
// RunObserveChildren.
type Callback func(entry KVEntry) error

// CallbackV2 type describes functions that receive mutations from
// RunObserveChildrenV2.
type CallbackV2 func(entry KVEntry) error

// RevCreate is a special revision which when passed to Set will change
// Set to Add.
var RevCreate = &struct{}{}

type store struct {
	url    *url.URL
	client *http.Client
}

var defaultStore = initDefaultStore()

func initDefaultStore() *store {
	c := *http.DefaultClient
	c.Transport = cbauth.WrapHTTPTransport(http.DefaultTransport, nil)

	authURL := os.Getenv("CBAUTH_REVRPC_URL")
	u, err := url.Parse(authURL)
	if err == nil {
		u.RawQuery = ""
		u.Fragment = ""
		u.Path = "/_metakv"
		u.User = nil
	}
	return &store{url: u, client: &c}
}

func doCallInner(s *store, method, path string, values url.Values) (resp *http.Response, err error) {
	var body io.Reader
	if method == "PUT" && values != nil {
		body = strings.NewReader(values.Encode())
	}
	url := *s.url
	url.Path += path
	req, err := http.NewRequest(method, url.String(), body)
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}
	if method != "PUT" && values != nil {
		req.URL.RawQuery = values.Encode()
	}
	r, err := s.client.Do(req)
	if err != nil {
		return r, err
	}
	if r.StatusCode == http.StatusConflict {
		return r, ErrRevMismatch
	}
	if r.StatusCode == http.StatusNotFound {
		return r, errNotFound
	}
	if r.StatusCode != 200 {
		return r, fmt.Errorf("ns_server _metakv returned: %s", r.Status)
	}
	return r, err
}

func doCall(s *store, method, path string, values url.Values) (body []byte, err error) {
	r, err := doCallInner(s, method, path, values)
	if r != nil {
		defer r.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(r.Body)
}

func doJSONCall(s *store, method, path string, values url.Values, place interface{}) error {
	body, err := doCall(s, method, path, values)
	if err != nil {
		return err
	}
	return json.Unmarshal(body, place)
}

type kvEntry struct {
	Path      string
	Value     []byte
	Rev       []byte
	Sensitive bool
}

type callbackInt func(entry kvEntry) error

func assertValidPathPrefix(path string) {
	if path[0] != '/' {
		panic("path must begin with /")
	}
}

func assertValidPath(path string) {
	assertValidPathPrefix(path)
	if strings.HasSuffix(path, "/") {
		panic("path must not end with \"/\"")
	}
}

func assertValidDirPath(path string) {
	assertValidPathPrefix(path)
	if !strings.HasSuffix(path, "/") {
		panic("dirpath must end with \"/\"")
	}
}

// Get returns matching value and revision for given key. Returns nil
// value, nil rev and nil error when given path doesn't exist.
func (s *store) get(path string) (value []byte, rev interface{}, err error) {
	assertValidPath(path)
	var kve kvEntry
	err = doJSONCall(s, "GET", path, nil, &kve)
	if err == errNotFound {
		return nil, nil, nil
	}
	if err != nil {
		return nil, nil, err
	}
	rev = nil
	if kve.Rev != nil {
		rev = kve.Rev
	}
	return kve.Value, rev, nil
}

func mutate(s *store, method string, path string, value []byte, rev interface{}, create bool, sensitive bool) error {
	values := url.Values{
		"value": {string(value)},
	}

	if create || rev == RevCreate {
		rev = nil
		values.Set("create", "1")
	}

	if rev != nil {
		revBytes, ok := rev.([]byte)
		if !ok {
			return ErrRevMismatch
		}
		values.Set("rev", string(revBytes))
	}

	if sensitive {
		values.Set("sensitive", "true")
	} else {
		values.Set("sensitive", "false")
	}

	_, err := doCall(s, method, path, values)
	return err
}

// Set updates given key-value pair. If non-nil, rev is a form of CAS
// value used to detect races with concurrent mutators in typical
// read-modify-write cases. Rev is supposed to be same value that is
// returned from get.
func (s *store) set(path string, value []byte, rev interface{}, sensitive bool) error {
	assertValidPath(path)
	return mutate(s, "PUT", path, value, rev, false, sensitive)
}

// Add creates given kv pair. Which must not exist in storage
// yet. ErrRevMismatch is returned if pair with such key exists.
func (s *store) add(path string, value []byte, sensitive bool) error {
	assertValidPath(path)
	return mutate(s, "PUT", path, value, nil, true, sensitive)
}

// Delete deletes given key.
func (s *store) delete(path string, rev interface{}) error {
	assertValidPath(path)
	return mutate(s, "DELETE", path, nil, rev, false, false)
}

// Recursive Delete deletes all keys that are children of given directory path.
func (s *store) recursiveDelete(dirpath string) error {
	assertValidDirPath(dirpath)
	return mutate(s, "DELETE", dirpath, nil, nil, false, false)
}

// IterateChildren invokes given callback on every kv-pair that's
// child of given directory path. Path must end on "/".
func (s *store) iterateChildren(dirpath string, callback callbackInt) error {
	return doRunObserveChildren(s, dirpath, callback, nil)
}

// RunObserveChildren invokes gen callback on every kv-pair that is
// child of given directory path and then on every mutation of
// affected keys. Deletions will be signalled by passing nil to value
// argument of callback. Returns only when cancel channel is closed or
// when children callback returns error. If exit is due to cancel
// channel being closed returned error is nil. Otherwise error is
// non-nil. Path must end on "/".
func (s *store) runObserveChildren(dirpath string, callback callbackInt,
	cancel <-chan struct{}) error {
	if cancel == nil {
		return nil
	}
	return doRunObserveChildren(s, dirpath, callback, cancel)
}

func doRunObserveChildren(s *store, dirpath string, callback callbackInt,
	cancel <-chan struct{}) error {
	assertValidDirPath(dirpath)
	values := url.Values{}
	if cancel != nil {
		values.Set("feed", "continuous")
	}
	r, err := doCallInner(s, "GET", dirpath, values)
	if r != nil {
		defer r.Body.Close()
	}
	if err != nil {
		return err
	}

	errChan := make(chan error, 1)
	kveChan := make(chan kvEntry)

	go func() {
		dec := json.NewDecoder(r.Body)
		var kve kvEntry
		for {
			err := dec.Decode(&kve)
			if err != nil {
				errChan <- err
				close(kveChan)
				return
			}
			kveChan <- kve
		}
	}()

	effCancel := cancel
	if cancel == nil {
		effCancel = make(chan struct{})
	}

readLoop:
	for {
		select {
		case kve, ok := <-kveChan:
			if !ok {
				err = <-errChan
				break readLoop
			}
			err = callback(kve)
			if err != nil {
				return err
			}

		case err = <-errChan:
			break readLoop

		case <-effCancel:
			r.Body.Close()
			// ensure that worker goroutine is done
			for range kveChan {
			}
			return nil
		}
	}

	if cancel == nil && err == io.EOF {
		err = nil
	}
	return err
}

// Get returns matching value and revision for given key. Returns nil
// value, nil rev and nil error when given path doesn't exist.
func Get(path string) (value []byte, rev interface{}, err error) {
	return defaultStore.get(path)
}

// Set updates given key-value pair. If non-nil, rev is a form of CAS
// value used to detect races with concurrent mutators in typical
// read-modify-write cases. Rev is supposed to be same value that is
// returned from get.
func Set(path string, value []byte, rev interface{}) error {
	return defaultStore.set(path, value, rev, false)
}

// SetSensitive is Set for storing sensitive info.
func SetSensitive(path string, value []byte, rev interface{}) error {
	return defaultStore.set(path, value, rev, true)
}

// Add creates given kv pair. Which must not exist in storage
// yet. ErrRevMismatch is returned if pair with such key exists.
func Add(path string, value []byte) error {
	return defaultStore.add(path, value, false)
}

// AddSensitive is Add for storing sensitive info.
func AddSensitive(path string, value []byte) error {
	return defaultStore.add(path, value, true)
}

// Delete deletes given key.
func Delete(path string, rev interface{}) error {
	return defaultStore.delete(path, rev)
}

// RecursiveDelete deletes all keys that are children of given directory path.
func RecursiveDelete(dirpath string) error {
	return defaultStore.recursiveDelete(dirpath)
}

// IterateChildren invokes given callback on every kv-pair that's
// child of given directory path. Path must end on "/".
func IterateChildren(dirpath string, callback Callback) error {
	return defaultStore.iterateChildren(dirpath,
		func(e kvEntry) error {
			return callback(KVEntry{e.Path, e.Value, e.Rev,
				e.Sensitive})
		})
}

// RunObserveChildren invokes gen callback on every kv-pair that is
// child of given directory path and then on every mutation of
// affected keys. Deletions will be signalled by passing nil to value
// argument of callback. Returns only when cancel channel is closed or
// when children callback returns error. If exit is due to cancel
// channel being closed returned error is nil. Otherwise error is
// non-nil. Path must end on "/".
func RunObserveChildren(dirpath string, callback Callback,
	cancel <-chan struct{}) error {
	return defaultStore.runObserveChildren(dirpath,
		func(e kvEntry) error {
			return callback(KVEntry{e.Path, e.Value, e.Rev,
				e.Sensitive})
		}, cancel)
}

// IterateChildrenV2 invokes given callback on every kv-pair that's
// child of given directory path. Path must end on "/".
func IterateChildrenV2(dirpath string, callback CallbackV2) error {
	return defaultStore.iterateChildren(dirpath,
		func(e kvEntry) error {
			return callback(KVEntry{e.Path, e.Value, e.Rev,
				e.Sensitive})
		})
}

// RunObserveChildrenV2 invokes gen callback on every kv-pair that is
// child of given directory path and then on every mutation of
// affected keys. Deletions will be signalled by passing nil to value
// argument of callback. Returns only when cancel channel is closed or
// when children callback returns error. If exit is due to cancel
// channel being closed returned error is nil. Otherwise error is
// non-nil. Path must end on "/".
func RunObserveChildrenV2(dirpath string, callback CallbackV2,
	cancel <-chan struct{}) error {
	return defaultStore.runObserveChildren(dirpath,
		func(e kvEntry) error {
			return callback(KVEntry{e.Path, e.Value, e.Rev,
				e.Sensitive})
		}, cancel)
}

// ListAllChildren returns all child entries of given "directory" node.
func ListAllChildren(dirpath string) (entries []KVEntry, err error) {
	return defaultStore.listAllChildren(dirpath)
}

// ListAllChildren returns all child entries of given "directory" node.
func (s *store) listAllChildren(dirpath string) (entries []KVEntry, err error) {
	// nil could be used here, but then empty list becomes nil and
	// in my testing code it means json null is returned rather
	// than empty json array.
	entries = make([]KVEntry, 0, 16)
	err = s.iterateChildren(dirpath,
		func(e kvEntry) error {
			entries = append(entries,
				KVEntry{e.Path, e.Value, e.Rev, e.Sensitive})
			return nil
		})
	return
}
<|endoftext|>


<commit_before>package controllers

import (
	"bufio"
	"html/template"
	"net"
	"net/http"
	"os"
	"strings"

	"github.com/onesimus-systems/packet-guardian/src/common"
	"github.com/onesimus-systems/packet-guardian/src/dhcp"
	"github.com/onesimus-systems/packet-guardian/src/models"
)

const (
	nonAdminAutoReg       = "na-auto"
	nonAdminManReg        = "na-man"
	nonAdminManRegNologin = "na-man-nologin"
	adminReg              = "admin"
)

type Manager struct {
	e *common.Environment
}

func NewManagerController(e *common.Environment) *Manager {
	return &Manager{e: e}
}

func (m *Manager) RegistrationHandler(w http.ResponseWriter, r *http.Request) {
	sessionUser := models.GetUserFromContext(r)
	session := common.GetSessionFromContext(r)
	flash := ""
	man := (r.FormValue("manual") == "1")
	loggedIn := session.GetBool("loggedin")
	ip := net.ParseIP(strings.Split(r.RemoteAddr, ":")[0])
	reg, _ := dhcp.IsRegisteredByIP(m.e, ip)
	if !man && reg {
		http.Redirect(w, r, "/manage", http.StatusTemporaryRedirect)
		return
	}

	formType := nonAdminAutoReg
	if man {
		if !m.e.Config.Registration.AllowManualRegistrations {
			flash = "Manual registrations are not allowed"
		} else {
			formType = nonAdminManReg
			if loggedIn {
				formType = nonAdminManRegNologin
			}
		}
	}

	username := sessionUser.Username
	if r.FormValue("username") != "" && sessionUser.IsAdmin() {
		username = r.FormValue("username")
		formType = adminReg
		flash = ""
	}

	// TODO: Don't show the registration page at all if blacklisted
	if formType == nonAdminAutoReg {
		mac, err := dhcp.GetMacFromIP(ip)
		if err != nil {
			m.e.Log.Errorf("Failed to get MAC from IP for %s", ip.String())
		} else {
			device, err := models.GetDeviceByMAC(m.e, mac)
			if err != nil {
				m.e.Log.Errorf("Error getting device for reg check: %s", err.Error())
			} else {
				if device.Blacklisted {
					flash = "The device appears to be blacklisted"
				}
			}
		}
	}

	session.AddFlash(flash)
	session.Save(r, w)
	data := map[string]interface{}{
		"policy":   m.loadPolicyText(),
		"type":     formType,
		"username": username,
	}

	if err := m.e.Views.NewView("register", r).Render(w, data); err != nil {
		m.e.Log.Error(err.Error())
	}
}

func (m *Manager) ManageHandler(w http.ResponseWriter, r *http.Request) {
	sessionUser := models.GetUserFromContext(r)

	results, err := models.GetDevicesForUser(m.e, sessionUser)
	if err != nil {
		m.e.Log.Errorf("Error getting devices for user %s: %s", sessionUser.Username, err.Error())
		// TODO: Show error page to user
		return
	}

	showAddBtn := (m.e.Config.Registration.AllowManualRegistrations && !sessionUser.IsBlacklisted())

	data := make(map[string]interface{})
	data["sessionUser"] = sessionUser
	data["devices"] = results
	data["showAddBtn"] = showAddBtn

	if err := m.e.Views.NewView("manage", r).Render(w, data); err != nil {
		m.e.Log.Error(err.Error())
	}
}

func (m *Manager) loadPolicyText() []template.HTML {
	f, err := os.Open(m.e.Config.Registration.RegistrationPolicyFile)
	if err != nil {
		return nil
	}
	defer f.Close()

	var policy []template.HTML
	currentParagraph := ""
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		t := strings.TrimSpace(scanner.Text())
		if t == "" {
			policy = append(policy, template.HTML(currentParagraph))
			currentParagraph = ""
			continue
		}
		currentParagraph += " " + t
	}
	policy = append(policy, template.HTML(currentParagraph))
	return policy
}
<commit_msg>Redirect admins/helpdesk users to the admin management page<commit_after>package controllers

import (
	"bufio"
	"html/template"
	"net"
	"net/http"
	"os"
	"strings"

	"github.com/onesimus-systems/packet-guardian/src/common"
	"github.com/onesimus-systems/packet-guardian/src/dhcp"
	"github.com/onesimus-systems/packet-guardian/src/models"
)

const (
	nonAdminAutoReg       = "na-auto"
	nonAdminManReg        = "na-man"
	nonAdminManRegNologin = "na-man-nologin"
	adminReg              = "admin"
)

type Manager struct {
	e *common.Environment
}

func NewManagerController(e *common.Environment) *Manager {
	return &Manager{e: e}
}

func (m *Manager) RegistrationHandler(w http.ResponseWriter, r *http.Request) {
	sessionUser := models.GetUserFromContext(r)
	session := common.GetSessionFromContext(r)
	flash := ""
	man := (r.FormValue("manual") == "1")
	loggedIn := session.GetBool("loggedin")
	ip := net.ParseIP(strings.Split(r.RemoteAddr, ":")[0])
	reg, _ := dhcp.IsRegisteredByIP(m.e, ip)
	if !man && reg {
		http.Redirect(w, r, "/manage", http.StatusTemporaryRedirect)
		return
	}

	formType := nonAdminAutoReg
	if man {
		if !m.e.Config.Registration.AllowManualRegistrations {
			flash = "Manual registrations are not allowed"
		} else {
			formType = nonAdminManReg
			if loggedIn {
				formType = nonAdminManRegNologin
			}
		}
	}

	username := sessionUser.Username
	if r.FormValue("username") != "" && sessionUser.IsAdmin() {
		username = r.FormValue("username")
		formType = adminReg
		flash = ""
	}

	// TODO: Don't show the registration page at all if blacklisted
	if formType == nonAdminAutoReg {
		mac, err := dhcp.GetMacFromIP(ip)
		if err != nil {
			m.e.Log.Errorf("Failed to get MAC from IP for %s", ip.String())
		} else {
			device, err := models.GetDeviceByMAC(m.e, mac)
			if err != nil {
				m.e.Log.Errorf("Error getting device for reg check: %s", err.Error())
			} else {
				if device.Blacklisted {
					flash = "The device appears to be blacklisted"
				}
			}
		}
	}

	session.AddFlash(flash)
	session.Save(r, w)
	data := map[string]interface{}{
		"policy":   m.loadPolicyText(),
		"type":     formType,
		"username": username,
	}

	if err := m.e.Views.NewView("register", r).Render(w, data); err != nil {
		m.e.Log.Error(err.Error())
	}
}

func (m *Manager) ManageHandler(w http.ResponseWriter, r *http.Request) {
	sessionUser := models.GetUserFromContext(r)

	if sessionUser.IsHelpDesk() {
		http.Redirect(w, r, "/admin/manage/"+sessionUser.Username, http.StatusTemporaryRedirect)
		return
	}

	results, err := models.GetDevicesForUser(m.e, sessionUser)
	if err != nil {
		m.e.Log.Errorf("Error getting devices for user %s: %s", sessionUser.Username, err.Error())
		// TODO: Show error page to user
		return
	}

	showAddBtn := (m.e.Config.Registration.AllowManualRegistrations && !sessionUser.IsBlacklisted())

	data := make(map[string]interface{})
	data["sessionUser"] = sessionUser
	data["devices"] = results
	data["showAddBtn"] = showAddBtn

	if err := m.e.Views.NewView("manage", r).Render(w, data); err != nil {
		m.e.Log.Error(err.Error())
	}
}

func (m *Manager) loadPolicyText() []template.HTML {
	f, err := os.Open(m.e.Config.Registration.RegistrationPolicyFile)
	if err != nil {
		return nil
	}
	defer f.Close()

	var policy []template.HTML
	currentParagraph := ""
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		t := strings.TrimSpace(scanner.Text())
		if t == "" {
			policy = append(policy, template.HTML(currentParagraph))
			currentParagraph = ""
			continue
		}
		currentParagraph += " " + t
	}
	policy = append(policy, template.HTML(currentParagraph))
	return policy
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Julien Vehent jvehent@mozilla.com [:ulfr]
// - Guillaume Destuynder <kang@mozilla.com>
package main

import (
	"fmt"
	"mig.ninja/mig"
	"mig.ninja/mig/pgp"
	"time"
)

// checkActionAuthorization verifies the PGP signatures of a given action
// against the Access Control List of the agent.
func checkActionAuthorization(a mig.Action, ctx *Context) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("checkActionAuthorization() -> %v", e)
		}
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "leaving checkActionAuthorization()"}.Debug()
	}()
	var keys [][]byte
	for _, pk := range PUBLICPGPKEYS {
		keys = append(keys, []byte(pk))
	}
	// get an io.Reader from the public pgp key
	keyring, keycount, err := pgp.ArmoredKeysToKeyring(keys)
	if err != nil {
		panic(err)
	}
	ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: fmt.Sprintf("loaded %d keys", keycount)}.Debug()

	// Check the action syntax and signature
	err = a.Validate()
	if err != nil {
		desc := fmt.Sprintf("action validation failed: %v", err)
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: desc}.Err()
		panic(desc)
	}
	// Validate() checks that the action hasn't expired, but we need to
	// check the start time ourselves
	if time.Now().Before(a.ValidFrom) {
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "action is scheduled for later"}.Err()
		panic("Action ValidFrom date is in the future")
	}

	// check ACLs, includes verifying signatures
	err = a.VerifyACL(ctx.ACL, keyring, VERIFYACLS)
	if err != nil {
		desc := fmt.Sprintf("action ACL verification failed: %v", err)
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: desc}.Err()
		panic(desc)
	}

	ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "ACL verification succeeded."}.Debug()
	return
}
<commit_msg>[minor] update call to VerifyAcls functions to include ONLYVERIFYPUBKEY global<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor:
// - Julien Vehent jvehent@mozilla.com [:ulfr]
// - Guillaume Destuynder <kang@mozilla.com>
package main

import (
	"fmt"
	"mig.ninja/mig"
	"mig.ninja/mig/pgp"
	"time"
)

// checkActionAuthorization verifies the PGP signatures of a given action
// against the Access Control List of the agent.
func checkActionAuthorization(a mig.Action, ctx *Context) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("checkActionAuthorization() -> %v", e)
		}
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "leaving checkActionAuthorization()"}.Debug()
	}()
	var keys [][]byte
	for _, pk := range PUBLICPGPKEYS {
		keys = append(keys, []byte(pk))
	}
	// get an io.Reader from the public pgp key
	keyring, keycount, err := pgp.ArmoredKeysToKeyring(keys)
	if err != nil {
		panic(err)
	}
	ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: fmt.Sprintf("loaded %d keys", keycount)}.Debug()

	// Check the action syntax and signature
	err = a.Validate()
	if err != nil {
		desc := fmt.Sprintf("action validation failed: %v", err)
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: desc}.Err()
		panic(desc)
	}
	// Validate() checks that the action hasn't expired, but we need to
	// check the start time ourselves
	if time.Now().Before(a.ValidFrom) {
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "action is scheduled for later"}.Err()
		panic("Action ValidFrom date is in the future")
	}

	// check ACLs, includes verifying signatures
	err = a.VerifyACL(ctx.ACL, keyring, ONLYVERIFYPUBKEY)
	if err != nil {
		desc := fmt.Sprintf("action ACL verification failed: %v", err)
		ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: desc}.Err()
		panic(desc)
	}

	ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "ACL verification succeeded."}.Debug()
	return
}
<|endoftext|>


<commit_before>package atomic_test

import (
	"errors"
	chaining "jecolasurdo/go-chaining"
	"jecolasurdo/go-chaining/internal/atomic"
	"testing"

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

func Test_ApplyUnaryIface_PreviousError_IgnoresAction(t *testing.T) {
	a := new(atomic.Context)
	c := new(chaining.Context)
	timesActionWasCalled := 0
	action := func(interface{}) (interface{}, error) {
		timesActionWasCalled++
		return nil, nil
	}

	c.LocalError = errors.New("test error")
	a.ApplyUnaryIface(action, &chaining.ActionArg{}, c)

	assert.Equal(t, 0, timesActionWasCalled)
}

func Test_ApplyUnaryIface_NoPreviousError_ExecutesAction(t *testing.T) {
	a := new(atomic.Context)
	c := new(chaining.Context)
	timesActionWasCalled := 0
	action := func(interface{}) (interface{}, error) {
		timesActionWasCalled++
		return nil, nil
	}

	a.ApplyUnaryIface(action, &chaining.ActionArg{}, c)

	assert.Equal(t, 1, timesActionWasCalled)
}

// func Test_ApplyUnaryIface_NoPreviousError_BehaviorIsNotSpecified_InjectsPreviousValue(t *testing.T) {
// 	d := new(chaining.Context)
// 	injectedValue := ""
// 	action := func(value interface{}) (interface{}, error) {
// 		injectedValue = value.(string)
// 		return nil, nil
// 	}
// 	argWithBehaviorNotSpecified := chaining.ActionArg{}
// 	simulatedValueOfPreviousActionInChain := "somevalue"
// 	d.PreviousActionResult = simulatedValueOfPreviousActionInChain

// 	d.ApplyUnaryIface(action, argWithBehaviorNotSpecified)

// 	assert.Equal(t, simulatedValueOfPreviousActionInChain, injectedValue)
// }

// func Test_ApplyUnaryIface_NoPreviousError_BehaviorIsUsePrevious_InjectsPreviousValue(t *testing.T) {
// 	d := new(chaining.Context)
// 	injectedValue := ""
// 	action := func(value interface{}) (interface{}, error) {
// 		injectedValue = value.(string)
// 		return nil, nil
// 	}
// 	argWithSpecifiedBehavior := chaining.ActionArg{
// 		Behavior: injectionbehavior.InjectPreviousResult,
// 	}
// 	simulatedValueOfPreviousActionInChain := "somevalue"
// 	d.PreviousActionResult = simulatedValueOfPreviousActionInChain

// 	d.ApplyUnaryIface(action, argWithSpecifiedBehavior)

// 	assert.Equal(t, simulatedValueOfPreviousActionInChain, injectedValue)
// }

// func Test_ApplyUnaryIface_NoPreviousError_BehaviorIsOverridePrevious_InjectsSuppliedValue(t *testing.T) {
// 	d := new(chaining.Context)
// 	injectedValue := ""
// 	action := func(value interface{}) (interface{}, error) {
// 		injectedValue = value.(string)
// 		return nil, nil
// 	}
// 	valueSubmittedThroughArg := "valueFromArg"
// 	argWithSpecifiedBehavior := chaining.ActionArg{
// 		Behavior: injectionbehavior.InjectSuppliedValue,
// 		Value:    valueSubmittedThroughArg,
// 	}
// 	simulatedValueOfPreviousActionInChain := "previousValue"
// 	d.PreviousActionResult = simulatedValueOfPreviousActionInChain

// 	d.ApplyUnaryIface(action, argWithSpecifiedBehavior)

// 	assert.Equal(t, valueSubmittedThroughArg, injectedValue)
// }

// func Test_ApplyUnaryIface_NoPreviousError_ForAnySpecifiedBehavior_SetsPreviousActionResult(t *testing.T) {
// 	d := new(chaining.Context)
// 	expectedReturnValue := "expectedReturnValue"
// 	action := func(value interface{}) (interface{}, error) { return expectedReturnValue, nil }
// 	arg := chaining.ActionArg{
// 		Value: "valueFromArg",
// 	}

// 	d.PreviousActionResult = nil
// 	arg.Behavior = injectionbehavior.InjectSuppliedValue
// 	d.ApplyUnaryIface(action, arg)
// 	assert.Equal(t, expectedReturnValue, d.PreviousActionResult)

// 	d.PreviousActionResult = nil
// 	arg.Behavior = injectionbehavior.InjectPreviousResult
// 	d.ApplyUnaryIface(action, arg)
// 	assert.Equal(t, expectedReturnValue, d.PreviousActionResult)

// 	d.PreviousActionResult = nil
// 	arg.Behavior = injectionbehavior.NotSpecified
// 	d.ApplyUnaryIface(action, arg)
// 	assert.Equal(t, expectedReturnValue, d.PreviousActionResult)
// }
<commit_msg>Passing third AUI function test.<commit_after>package atomic_test

import (
	"errors"
	chaining "jecolasurdo/go-chaining"
	"jecolasurdo/go-chaining/internal/atomic"
	"testing"

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

func Test_ApplyUnaryIface_PreviousError_IgnoresAction(t *testing.T) {
	a := new(atomic.Context)
	c := new(chaining.Context)
	timesActionWasCalled := 0
	action := func(interface{}) (interface{}, error) {
		timesActionWasCalled++
		return nil, nil
	}

	c.LocalError = errors.New("test error")
	a.ApplyUnaryIface(action, &chaining.ActionArg{}, c)

	assert.Equal(t, 0, timesActionWasCalled)
}

func Test_ApplyUnaryIface_NoPreviousError_ExecutesAction(t *testing.T) {
	a := new(atomic.Context)
	c := new(chaining.Context)
	timesActionWasCalled := 0
	action := func(interface{}) (interface{}, error) {
		timesActionWasCalled++
		return nil, nil
	}

	a.ApplyUnaryIface(action, &chaining.ActionArg{}, c)

	assert.Equal(t, 1, timesActionWasCalled)
}

func Test_ApplyUnaryIface_NoPreviousError_BehaviorIsNotSpecified_InjectsPreviousValue(t *testing.T) {
	a := new(atomic.Context)
	c := new(chaining.Context)
	injectedValue := ""
	action := func(value interface{}) (interface{}, error) {
		injectedValue = value.(string)
		return nil, nil
	}
	argWithBehaviorNotSpecified := chaining.ActionArg{}
	simulatedValueOfPreviousActionInChain := "somevalue"
	c.PreviousActionResult = simulatedValueOfPreviousActionInChain

	a.ApplyUnaryIface(action, &argWithBehaviorNotSpecified, c)

	assert.Equal(t, simulatedValueOfPreviousActionInChain, injectedValue)
}

// func Test_ApplyUnaryIface_NoPreviousError_BehaviorIsUsePrevious_InjectsPreviousValue(t *testing.T) {
// 	d := new(chaining.Context)
// 	injectedValue := ""
// 	action := func(value interface{}) (interface{}, error) {
// 		injectedValue = value.(string)
// 		return nil, nil
// 	}
// 	argWithSpecifiedBehavior := chaining.ActionArg{
// 		Behavior: injectionbehavior.InjectPreviousResult,
// 	}
// 	simulatedValueOfPreviousActionInChain := "somevalue"
// 	d.PreviousActionResult = simulatedValueOfPreviousActionInChain

// 	d.ApplyUnaryIface(action, argWithSpecifiedBehavior)

// 	assert.Equal(t, simulatedValueOfPreviousActionInChain, injectedValue)
// }

// func Test_ApplyUnaryIface_NoPreviousError_BehaviorIsOverridePrevious_InjectsSuppliedValue(t *testing.T) {
// 	d := new(chaining.Context)
// 	injectedValue := ""
// 	action := func(value interface{}) (interface{}, error) {
// 		injectedValue = value.(string)
// 		return nil, nil
// 	}
// 	valueSubmittedThroughArg := "valueFromArg"
// 	argWithSpecifiedBehavior := chaining.ActionArg{
// 		Behavior: injectionbehavior.InjectSuppliedValue,
// 		Value:    valueSubmittedThroughArg,
// 	}
// 	simulatedValueOfPreviousActionInChain := "previousValue"
// 	d.PreviousActionResult = simulatedValueOfPreviousActionInChain

// 	d.ApplyUnaryIface(action, argWithSpecifiedBehavior)

// 	assert.Equal(t, valueSubmittedThroughArg, injectedValue)
// }

// func Test_ApplyUnaryIface_NoPreviousError_ForAnySpecifiedBehavior_SetsPreviousActionResult(t *testing.T) {
// 	d := new(chaining.Context)
// 	expectedReturnValue := "expectedReturnValue"
// 	action := func(value interface{}) (interface{}, error) { return expectedReturnValue, nil }
// 	arg := chaining.ActionArg{
// 		Value: "valueFromArg",
// 	}

// 	d.PreviousActionResult = nil
// 	arg.Behavior = injectionbehavior.InjectSuppliedValue
// 	d.ApplyUnaryIface(action, arg)
// 	assert.Equal(t, expectedReturnValue, d.PreviousActionResult)

// 	d.PreviousActionResult = nil
// 	arg.Behavior = injectionbehavior.InjectPreviousResult
// 	d.ApplyUnaryIface(action, arg)
// 	assert.Equal(t, expectedReturnValue, d.PreviousActionResult)

// 	d.PreviousActionResult = nil
// 	arg.Behavior = injectionbehavior.NotSpecified
// 	d.ApplyUnaryIface(action, arg)
// 	assert.Equal(t, expectedReturnValue, d.PreviousActionResult)
// }
<|endoftext|>