<commit_before>package db

import (
	"fmt"
	"path"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/opt"

	. "github.com/tendermint/go-common"
)

func init() {
	dbCreator := func(name string, dir string) (DB, error) {
		return NewGoLevelDB(name, dir)
	}
	registerDBCreator(LevelDBBackendStr, dbCreator, false)
	registerDBCreator(GoLevelDBBackendStr, dbCreator, false)
}

type GoLevelDB struct {
	db *leveldb.DB
}

func NewGoLevelDB(name string, dir string) (*GoLevelDB, error) {
	dbPath := path.Join(dir, name+".db")
	db, err := leveldb.OpenFile(dbPath, nil)
	if err != nil {
		return nil, err
	}
	database := &GoLevelDB{db: db}
	return database, nil
}

func (db *GoLevelDB) Get(key []byte) []byte {
	res, err := db.db.Get(key, nil)
	if err != nil {
		if err == errors.ErrNotFound {
			return nil
		} else {
			PanicCrisis(err)
		}
	}
	return res
}

func (db *GoLevelDB) Set(key []byte, value []byte) {
	err := db.db.Put(key, value, nil)
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) SetSync(key []byte, value []byte) {
	err := db.db.Put(key, value, &opt.WriteOptions{Sync: true})
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) Delete(key []byte) {
	err := db.db.Delete(key, nil)
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) DeleteSync(key []byte) {
	err := db.db.Delete(key, &opt.WriteOptions{Sync: true})
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) DB() *leveldb.DB {
	return db.db
}

func (db *GoLevelDB) Close() {
	db.db.Close()
}

func (db *GoLevelDB) Print() {
	str, _ := db.db.GetProperty("leveldb.stats")
	fmt.Printf("%v\n", str)

	iter := db.db.NewIterator(nil, nil)
	for iter.Next() {
		key := iter.Key()
		value := iter.Value()
		fmt.Printf("[%X]:\t[%X]\n", key, value)
	}
}

func (db *GoLevelDB) Stats() map[string]string {
	keys := []string{
		"leveldb.num-files-at-level{n}",
		"leveldb.stats",
		"leveldb.sstables",
		"leveldb.blockpool",
		"leveldb.cachedblock",
		"leveldb.openedtables",
		"leveldb.alivesnaps",
		"leveldb.alibeiters",
	}

	stats := make(map[string]string)
	for _, key := range keys {
		str, err := db.db.GetProperty(key)
		if err == nil {
			stats[key] = str
		}
	}
	return stats
}

func (db *GoLevelDB) Iterator() Iterator {
	return db.db.NewIterator(nil, nil)
}

func (db *GoLevelDB) NewBatch() Batch {
	batch := new(leveldb.Batch)
	return &goLevelDBBatch{db, batch}
}

//--------------------------------------------------------------------------------

type goLevelDBBatch struct {
	db    *GoLevelDB
	batch *leveldb.Batch
}

func (mBatch *goLevelDBBatch) Set(key, value []byte) {
	mBatch.batch.Put(key, value)
}

func (mBatch *goLevelDBBatch) Delete(key []byte) {
	mBatch.batch.Delete(key)
}

func (mBatch *goLevelDBBatch) Write() {
	err := mBatch.db.db.Write(mBatch.batch, nil)
	if err != nil {
		PanicCrisis(err)
	}
}
<commit_msg>Fixed a typo in LevelDB property names.<commit_after>package db

import (
	"fmt"
	"path"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/opt"

	. "github.com/tendermint/go-common"
)

func init() {
	dbCreator := func(name string, dir string) (DB, error) {
		return NewGoLevelDB(name, dir)
	}
	registerDBCreator(LevelDBBackendStr, dbCreator, false)
	registerDBCreator(GoLevelDBBackendStr, dbCreator, false)
}

type GoLevelDB struct {
	db *leveldb.DB
}

func NewGoLevelDB(name string, dir string) (*GoLevelDB, error) {
	dbPath := path.Join(dir, name+".db")
	db, err := leveldb.OpenFile(dbPath, nil)
	if err != nil {
		return nil, err
	}
	database := &GoLevelDB{db: db}
	return database, nil
}

func (db *GoLevelDB) Get(key []byte) []byte {
	res, err := db.db.Get(key, nil)
	if err != nil {
		if err == errors.ErrNotFound {
			return nil
		} else {
			PanicCrisis(err)
		}
	}
	return res
}

func (db *GoLevelDB) Set(key []byte, value []byte) {
	err := db.db.Put(key, value, nil)
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) SetSync(key []byte, value []byte) {
	err := db.db.Put(key, value, &opt.WriteOptions{Sync: true})
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) Delete(key []byte) {
	err := db.db.Delete(key, nil)
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) DeleteSync(key []byte) {
	err := db.db.Delete(key, &opt.WriteOptions{Sync: true})
	if err != nil {
		PanicCrisis(err)
	}
}

func (db *GoLevelDB) DB() *leveldb.DB {
	return db.db
}

func (db *GoLevelDB) Close() {
	db.db.Close()
}

func (db *GoLevelDB) Print() {
	str, _ := db.db.GetProperty("leveldb.stats")
	fmt.Printf("%v\n", str)

	iter := db.db.NewIterator(nil, nil)
	for iter.Next() {
		key := iter.Key()
		value := iter.Value()
		fmt.Printf("[%X]:\t[%X]\n", key, value)
	}
}

func (db *GoLevelDB) Stats() map[string]string {
	keys := []string{
		"leveldb.num-files-at-level{n}",
		"leveldb.stats",
		"leveldb.sstables",
		"leveldb.blockpool",
		"leveldb.cachedblock",
		"leveldb.openedtables",
		"leveldb.alivesnaps",
		"leveldb.aliveiters",
	}

	stats := make(map[string]string)
	for _, key := range keys {
		str, err := db.db.GetProperty(key)
		if err == nil {
			stats[key] = str
		}
	}
	return stats
}

func (db *GoLevelDB) Iterator() Iterator {
	return db.db.NewIterator(nil, nil)
}

func (db *GoLevelDB) NewBatch() Batch {
	batch := new(leveldb.Batch)
	return &goLevelDBBatch{db, batch}
}

//--------------------------------------------------------------------------------

type goLevelDBBatch struct {
	db    *GoLevelDB
	batch *leveldb.Batch
}

func (mBatch *goLevelDBBatch) Set(key, value []byte) {
	mBatch.batch.Put(key, value)
}

func (mBatch *goLevelDBBatch) Delete(key []byte) {
	mBatch.batch.Delete(key)
}

func (mBatch *goLevelDBBatch) Write() {
	err := mBatch.db.db.Write(mBatch.batch, nil)
	if err != nil {
		PanicCrisis(err)
	}
}
<|endoftext|>


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

package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/Sirupsen/logrus/hooks/syslog"
	"github.com/jessevdk/go-flags"
	p "github.com/kr/pretty"
	"github.com/osrg/gobgp/config"
	ops "github.com/osrg/gobgp/openswitch"
	"github.com/osrg/gobgp/server"
	"io/ioutil"
	"log/syslog"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
)

func main() {
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM)

	var opts struct {
		ConfigFile      string `short:"f" long:"config-file" description:"specifying a config file"`
		ConfigType      string `short:"t" long:"config-type" description:"specifying config type (toml, yaml, json)" default:"toml"`
		LogLevel        string `short:"l" long:"log-level" description:"specifying log level"`
		LogPlain        bool   `short:"p" long:"log-plain" description:"use plain format for logging (json by default)"`
		UseSyslog       string `short:"s" long:"syslog" description:"use syslogd"`
		Facility        string `long:"syslog-facility" description:"specify syslog facility"`
		DisableStdlog   bool   `long:"disable-stdlog" description:"disable standard logging"`
		CPUs            int    `long:"cpus" description:"specify the number of CPUs to be used"`
		Ops             bool   `long:"openswitch" description:"openswitch mode"`
		GrpcPort        int    `short:"g" long:"grpc-port" description:"grpc port" default:"50051"`
		GracefulRestart bool   `short:"r" long:"graceful-restart" description:"flag restart-state in graceful-restart capability"`
		Dry             bool   `short:"d" long:"dry-run" description:"check configuration"`
	}
	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(1)
	}

	if opts.CPUs == 0 {
		runtime.GOMAXPROCS(runtime.NumCPU())
	} else {
		if runtime.NumCPU() < opts.CPUs {
			log.Errorf("Only %d CPUs are available but %d is specified", runtime.NumCPU(), opts.CPUs)
			os.Exit(1)
		}
		runtime.GOMAXPROCS(opts.CPUs)
	}

	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	switch opts.LogLevel {
	case "debug":
		log.SetLevel(log.DebugLevel)
	case "info":
		log.SetLevel(log.InfoLevel)
	default:
		log.SetLevel(log.InfoLevel)
	}

	if opts.DisableStdlog == true {
		log.SetOutput(ioutil.Discard)
	} else {
		log.SetOutput(os.Stdout)
	}

	if opts.UseSyslog != "" {
		dst := strings.SplitN(opts.UseSyslog, ":", 2)
		network := ""
		addr := ""
		if len(dst) == 2 {
			network = dst[0]
			addr = dst[1]
		}

		facility := syslog.Priority(0)
		switch opts.Facility {
		case "kern":
			facility = syslog.LOG_KERN
		case "user":
			facility = syslog.LOG_USER
		case "mail":
			facility = syslog.LOG_MAIL
		case "daemon":
			facility = syslog.LOG_DAEMON
		case "auth":
			facility = syslog.LOG_AUTH
		case "syslog":
			facility = syslog.LOG_SYSLOG
		case "lpr":
			facility = syslog.LOG_LPR
		case "news":
			facility = syslog.LOG_NEWS
		case "uucp":
			facility = syslog.LOG_UUCP
		case "cron":
			facility = syslog.LOG_CRON
		case "authpriv":
			facility = syslog.LOG_AUTHPRIV
		case "ftp":
			facility = syslog.LOG_FTP
		case "local0":
			facility = syslog.LOG_LOCAL0
		case "local1":
			facility = syslog.LOG_LOCAL1
		case "local2":
			facility = syslog.LOG_LOCAL2
		case "local3":
			facility = syslog.LOG_LOCAL3
		case "local4":
			facility = syslog.LOG_LOCAL4
		case "local5":
			facility = syslog.LOG_LOCAL5
		case "local6":
			facility = syslog.LOG_LOCAL6
		case "local7":
			facility = syslog.LOG_LOCAL7
		}

		hook, err := logrus_syslog.NewSyslogHook(network, addr, syslog.LOG_INFO|facility, "bgpd")
		if err != nil {
			log.Error("Unable to connect to syslog daemon, ", opts.UseSyslog)
			os.Exit(1)
		} else {
			log.AddHook(hook)
		}
	}

	if opts.LogPlain {
		if opts.DisableStdlog {
			log.SetFormatter(&log.TextFormatter{
				DisableColors: true,
			})
		}
	} else {
		log.SetFormatter(&log.JSONFormatter{})
	}

	configCh := make(chan config.BgpConfigSet)
	reloadCh := make(chan bool)
	if opts.Dry {
		go config.ReadConfigfileServe(opts.ConfigFile, opts.ConfigType, configCh, reloadCh)
		reloadCh <- true
		c := <-configCh
		if opts.LogLevel == "debug" {
			p.Println(c)
		}
		os.Exit(0)
	}

	log.Info("gobgpd started")

	bgpServer := server.NewBgpServer()
	if opts.Ops {
		m, err := ops.NewOpsManager(bgpServer.GrpcReqCh)
		if err != nil {
			log.Errorf("Failed to start ops config manager: %s", err)
			os.Exit(1)
		}
		log.Info("Coordination with OpenSwitch")
		m.Serve()
	} else if opts.ConfigFile != "" {
		go config.ReadConfigfileServe(opts.ConfigFile, opts.ConfigType, configCh, reloadCh)
		reloadCh <- true
	}
	go bgpServer.Serve()

	// start grpc Server
	grpcServer := server.NewGrpcServer(opts.GrpcPort, bgpServer.GrpcReqCh)
	go func() {
		if err := grpcServer.Serve(); err != nil {
			log.Fatalf("failed to listen grpc port: %s", err)
		}
	}()

	var bgpConfig *config.Bgp = nil
	var policyConfig *config.RoutingPolicy = nil
	for {
		select {
		case newConfig := <-configCh:
			var added, deleted, updated []config.Neighbor

			if bgpConfig == nil {
				bgpServer.SetGlobalType(newConfig.Bgp.Global)
				bgpConfig = &newConfig.Bgp
				bgpServer.SetRpkiConfig(newConfig.Bgp.RpkiServers)
				added = newConfig.Bgp.Neighbors
				if opts.GracefulRestart {
					for i, n := range added {
						if n.GracefulRestart.Config.Enabled {
							added[i].GracefulRestart.State.LocalRestarting = true
						}
					}
				}
				deleted = []config.Neighbor{}
				updated = []config.Neighbor{}
			} else {
				bgpConfig, added, deleted, updated = config.UpdateConfig(bgpConfig, &newConfig.Bgp)
			}

			if policyConfig == nil {
				policyConfig = &newConfig.Policy
				// FIXME: Currently the following code
				// is safe because the above
				// SetRpkiConfig will be blocked
				// because the length of rpkiConfigCh
				// is zero. So server.GlobalRib is
				// allocated before the above
				// SetPolicy. But this should be
				// handled more cleanly.
				if err := bgpServer.SetRoutingPolicy(newConfig.Policy); err != nil {
					log.Fatal(err)
				}
			} else {
				if config.CheckPolicyDifference(policyConfig, &newConfig.Policy) {
					log.Info("Policy config is updated")
					bgpServer.UpdatePolicy(newConfig.Policy)
					policyConfig = &newConfig.Policy
				}
			}

			for _, p := range added {
				log.Infof("Peer %v is added", p.Config.NeighborAddress)
				bgpServer.PeerAdd(p)
			}
			for _, p := range deleted {
				log.Infof("Peer %v is deleted", p.Config.NeighborAddress)
				bgpServer.PeerDelete(p)
			}
			for _, p := range updated {
				log.Infof("Peer %v is updated", p.Config.NeighborAddress)
				bgpServer.PeerUpdate(p)
			}
		case sig := <-sigCh:
			switch sig {
			case syscall.SIGHUP:
				log.Info("reload the config file")
				reloadCh <- true
			case syscall.SIGKILL, syscall.SIGTERM:
				bgpServer.Shutdown()
			}
		}
	}
}
<commit_msg>gobgpd: support force GC and return free memory to OS<commit_after>// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/Sirupsen/logrus/hooks/syslog"
	"github.com/jessevdk/go-flags"
	p "github.com/kr/pretty"
	"github.com/osrg/gobgp/config"
	ops "github.com/osrg/gobgp/openswitch"
	"github.com/osrg/gobgp/server"
	"io/ioutil"
	"log/syslog"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"runtime/debug"
	"strings"
	"syscall"
)

func main() {
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGUSR1)

	var opts struct {
		ConfigFile      string `short:"f" long:"config-file" description:"specifying a config file"`
		ConfigType      string `short:"t" long:"config-type" description:"specifying config type (toml, yaml, json)" default:"toml"`
		LogLevel        string `short:"l" long:"log-level" description:"specifying log level"`
		LogPlain        bool   `short:"p" long:"log-plain" description:"use plain format for logging (json by default)"`
		UseSyslog       string `short:"s" long:"syslog" description:"use syslogd"`
		Facility        string `long:"syslog-facility" description:"specify syslog facility"`
		DisableStdlog   bool   `long:"disable-stdlog" description:"disable standard logging"`
		CPUs            int    `long:"cpus" description:"specify the number of CPUs to be used"`
		Ops             bool   `long:"openswitch" description:"openswitch mode"`
		GrpcPort        int    `short:"g" long:"grpc-port" description:"grpc port" default:"50051"`
		GracefulRestart bool   `short:"r" long:"graceful-restart" description:"flag restart-state in graceful-restart capability"`
		Dry             bool   `short:"d" long:"dry-run" description:"check configuration"`
	}
	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(1)
	}

	if opts.CPUs == 0 {
		runtime.GOMAXPROCS(runtime.NumCPU())
	} else {
		if runtime.NumCPU() < opts.CPUs {
			log.Errorf("Only %d CPUs are available but %d is specified", runtime.NumCPU(), opts.CPUs)
			os.Exit(1)
		}
		runtime.GOMAXPROCS(opts.CPUs)
	}

	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	switch opts.LogLevel {
	case "debug":
		log.SetLevel(log.DebugLevel)
	case "info":
		log.SetLevel(log.InfoLevel)
	default:
		log.SetLevel(log.InfoLevel)
	}

	if opts.DisableStdlog == true {
		log.SetOutput(ioutil.Discard)
	} else {
		log.SetOutput(os.Stdout)
	}

	if opts.UseSyslog != "" {
		dst := strings.SplitN(opts.UseSyslog, ":", 2)
		network := ""
		addr := ""
		if len(dst) == 2 {
			network = dst[0]
			addr = dst[1]
		}

		facility := syslog.Priority(0)
		switch opts.Facility {
		case "kern":
			facility = syslog.LOG_KERN
		case "user":
			facility = syslog.LOG_USER
		case "mail":
			facility = syslog.LOG_MAIL
		case "daemon":
			facility = syslog.LOG_DAEMON
		case "auth":
			facility = syslog.LOG_AUTH
		case "syslog":
			facility = syslog.LOG_SYSLOG
		case "lpr":
			facility = syslog.LOG_LPR
		case "news":
			facility = syslog.LOG_NEWS
		case "uucp":
			facility = syslog.LOG_UUCP
		case "cron":
			facility = syslog.LOG_CRON
		case "authpriv":
			facility = syslog.LOG_AUTHPRIV
		case "ftp":
			facility = syslog.LOG_FTP
		case "local0":
			facility = syslog.LOG_LOCAL0
		case "local1":
			facility = syslog.LOG_LOCAL1
		case "local2":
			facility = syslog.LOG_LOCAL2
		case "local3":
			facility = syslog.LOG_LOCAL3
		case "local4":
			facility = syslog.LOG_LOCAL4
		case "local5":
			facility = syslog.LOG_LOCAL5
		case "local6":
			facility = syslog.LOG_LOCAL6
		case "local7":
			facility = syslog.LOG_LOCAL7
		}

		hook, err := logrus_syslog.NewSyslogHook(network, addr, syslog.LOG_INFO|facility, "bgpd")
		if err != nil {
			log.Error("Unable to connect to syslog daemon, ", opts.UseSyslog)
			os.Exit(1)
		} else {
			log.AddHook(hook)
		}
	}

	if opts.LogPlain {
		if opts.DisableStdlog {
			log.SetFormatter(&log.TextFormatter{
				DisableColors: true,
			})
		}
	} else {
		log.SetFormatter(&log.JSONFormatter{})
	}

	configCh := make(chan config.BgpConfigSet)
	reloadCh := make(chan bool)
	if opts.Dry {
		go config.ReadConfigfileServe(opts.ConfigFile, opts.ConfigType, configCh, reloadCh)
		reloadCh <- true
		c := <-configCh
		if opts.LogLevel == "debug" {
			p.Println(c)
		}
		os.Exit(0)
	}

	log.Info("gobgpd started")

	bgpServer := server.NewBgpServer()
	if opts.Ops {
		m, err := ops.NewOpsManager(bgpServer.GrpcReqCh)
		if err != nil {
			log.Errorf("Failed to start ops config manager: %s", err)
			os.Exit(1)
		}
		log.Info("Coordination with OpenSwitch")
		m.Serve()
	} else if opts.ConfigFile != "" {
		go config.ReadConfigfileServe(opts.ConfigFile, opts.ConfigType, configCh, reloadCh)
		reloadCh <- true
	}
	go bgpServer.Serve()

	// start grpc Server
	grpcServer := server.NewGrpcServer(opts.GrpcPort, bgpServer.GrpcReqCh)
	go func() {
		if err := grpcServer.Serve(); err != nil {
			log.Fatalf("failed to listen grpc port: %s", err)
		}
	}()

	var bgpConfig *config.Bgp = nil
	var policyConfig *config.RoutingPolicy = nil
	for {
		select {
		case newConfig := <-configCh:
			var added, deleted, updated []config.Neighbor

			if bgpConfig == nil {
				bgpServer.SetGlobalType(newConfig.Bgp.Global)
				bgpConfig = &newConfig.Bgp
				bgpServer.SetRpkiConfig(newConfig.Bgp.RpkiServers)
				added = newConfig.Bgp.Neighbors
				if opts.GracefulRestart {
					for i, n := range added {
						if n.GracefulRestart.Config.Enabled {
							added[i].GracefulRestart.State.LocalRestarting = true
						}
					}
				}
				deleted = []config.Neighbor{}
				updated = []config.Neighbor{}
			} else {
				bgpConfig, added, deleted, updated = config.UpdateConfig(bgpConfig, &newConfig.Bgp)
			}

			if policyConfig == nil {
				policyConfig = &newConfig.Policy
				// FIXME: Currently the following code
				// is safe because the above
				// SetRpkiConfig will be blocked
				// because the length of rpkiConfigCh
				// is zero. So server.GlobalRib is
				// allocated before the above
				// SetPolicy. But this should be
				// handled more cleanly.
				if err := bgpServer.SetRoutingPolicy(newConfig.Policy); err != nil {
					log.Fatal(err)
				}
			} else {
				if config.CheckPolicyDifference(policyConfig, &newConfig.Policy) {
					log.Info("Policy config is updated")
					bgpServer.UpdatePolicy(newConfig.Policy)
					policyConfig = &newConfig.Policy
				}
			}

			for _, p := range added {
				log.Infof("Peer %v is added", p.Config.NeighborAddress)
				bgpServer.PeerAdd(p)
			}
			for _, p := range deleted {
				log.Infof("Peer %v is deleted", p.Config.NeighborAddress)
				bgpServer.PeerDelete(p)
			}
			for _, p := range updated {
				log.Infof("Peer %v is updated", p.Config.NeighborAddress)
				bgpServer.PeerUpdate(p)
			}
		case sig := <-sigCh:
			switch sig {
			case syscall.SIGHUP:
				log.Info("reload the config file")
				reloadCh <- true
			case syscall.SIGKILL, syscall.SIGTERM:
				bgpServer.Shutdown()
			case syscall.SIGUSR1:
				runtime.GC()
				debug.FreeOSMemory()
			}
		}
	}
}
<|endoftext|>


<commit_before>package godash

import (
	"reflect"
	"testing"
)

func TestIsPointer(t *testing.T) {
	x := &[]string{}
	y := []string{}

	if r := IsPointer(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsFunction(t *testing.T) {
	x := func() {}
	y := []string{}

	if r := IsFunction(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsSlice(t *testing.T) {
	x := []string{}
	y := func() {}

	if r := IsSlice(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsSlice(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsMap(t *testing.T) {
	x := make(map[string]interface{})
	y := 4
	z := make([]string, 0)

	if r := IsMap(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsMap(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r := IsMap(z); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestUnique(t *testing.T) {
	in := []string{"a", "a", "c", "d", "c"}
	out := []string{}
	b1 := []int{}

	// Test argument types
	if err := Unique(in, b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := Unique(in, &b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if err := Unique(in, &out); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if len(out) != 3 {
		t.Errorf("expected output slice length of 3 (got %v)", len(out))
	}
}

func TestSliceEqual(t *testing.T) {
	s1 := []string{"a", "b", "c"}
	s2 := []string{"c", "b", "a"}
	s3 := []string{"a", "b", "d", "c"}
	s4 := []int{1, 2, 3}
	x := 0

	// Test argument types
	if _, err := SliceEqual(s1, x); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := SliceEqual(s1, s4); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := SliceEqual(s1, x); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r, _ := SliceEqual(s1, s2); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s2); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s3); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s3); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s4); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIncludes(t *testing.T) {
	s1 := []string{"a", "b", "c"}

	// Test argument types
	if _, err := Includes(s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := Includes(1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := Includes(s1, "a"); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r, _ := Includes(s1, "d"); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestAppendUniq(t *testing.T) {
	// Test argument types
	x := 1
	s1 := []string{"a", "b", "c"}

	if err := AppendUniq(&x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, "d", 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	s2 := []string{"a", "b", "c"}
	if err := AppendUniq(&s2, "a", "d", "a", "d"); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r := reflect.DeepEqual(s2, []string{"a", "b", "c", "d"}); !r {
		t.Errorf("expected correct slice (got %v)", s2)
	}
}

func TestKeys(t *testing.T) {
	x := map[string]interface{}{"a": 3, "b": false}
	a := []int{}
	b := 3
	c := []string{}

	// Test argument types
	if err := MapKeys(&x, a); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(a, a); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(x, a); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(x, &a); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(x, b); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(x, &b); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []string{"a", "b"}
	if err := MapKeys(x, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}
<commit_msg>Update unit tests for map-based operations<commit_after>package godash

import (
	"reflect"
	"testing"
)

func TestIsPointer(t *testing.T) {
	x := &[]string{}
	y := []string{}

	if r := IsPointer(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsFunction(t *testing.T) {
	x := func() {}
	y := []string{}

	if r := IsFunction(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsSlice(t *testing.T) {
	x := []string{}
	y := func() {}

	if r := IsSlice(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsSlice(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsMap(t *testing.T) {
	x := make(map[string]interface{})
	y := 4
	z := make([]string, 0)

	if r := IsMap(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsMap(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r := IsMap(z); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestUnique(t *testing.T) {
	in := []string{"a", "a", "c", "d", "c"}
	out := []string{}
	b1 := []int{}

	// Test argument types
	if err := Unique(in, b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := Unique(in, &b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if err := Unique(in, &out); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if len(out) != 3 {
		t.Errorf("expected output slice length of 3 (got %v)", len(out))
	}
}

func TestSliceEqual(t *testing.T) {
	s1 := []string{"a", "b", "c"}
	s2 := []string{"c", "b", "a"}
	s3 := []string{"a", "b", "d", "c"}
	s4 := []int{1, 2, 3}
	x := 0

	// Test argument types
	if _, err := SliceEqual(s1, x); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := SliceEqual(s1, s4); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := SliceEqual(s1, x); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r, _ := SliceEqual(s1, s2); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s2); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s3); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s3); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s4); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIncludes(t *testing.T) {
	s1 := []string{"a", "b", "c"}

	// Test argument types
	if _, err := Includes(s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := Includes(1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := Includes(s1, "a"); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r, _ := Includes(s1, "d"); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestAppendUniq(t *testing.T) {
	// Test argument types
	x := 1
	s1 := []string{"a", "b", "c"}

	if err := AppendUniq(&x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, "d", 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	s2 := []string{"a", "b", "c"}
	if err := AppendUniq(&s2, "a", "d", "a", "d"); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r := reflect.DeepEqual(s2, []string{"a", "b", "c", "d"}); !r {
		t.Errorf("expected correct slice (got %v)", s2)
	}
}

func TestKeys(t *testing.T) {
	m := map[string]interface{}{"a": 3, "b": false}
	a := []int{}
	b := 3
	c := []string{}

	// Test argument types
	if err := MapKeys(&m, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(a, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, a); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, &a); err == nil {
		// Test the output variable is a pointer of the valid type
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, &b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []string{"a", "b"}
	if err := MapKeys(m, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}

func TestMapValues(t *testing.T) {
	m := map[string]int{"foo": 3, "bar": 6}
	a := []string{}
	b := 3
	c := []int{}

	// Test argument types
	if err := MapValues(&m, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(a, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, a); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, &a); err == nil {
		// Test the output variable is a pointer of the valid type
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, &b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []int{3, 6}
	if err := MapValues(m, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}
<|endoftext|>


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

// Devweb is a simple environment for developing a web server.
// It runs its own web server on the given address and proxies
// all requests to the http server program named by importpath.
// It takes care of recompiling and restarting the program as needed.
//
// The server program should be a trivial main program like:
//
//	package main
//
//	import (
//		"code.google.com/p/rsc/devweb/slave"
//
//		_ "this/package"
//		_ "that/package"
//	)
//
//	func main() {
//		slave.Main()
//	}
//
// The import _ lines import packages that register HTTP handlers,
// like in an App Engine program.
//
// As you make changes to this/package or that/package (or their
// dependencies), devweb recompiles and relaunches the servers as
// needed to serve requests.
//
package main

// BUG(rsc): Devweb should probably

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

func usage() {
	fmt.Fprint(os.Stderr, `usage: devweb [-addr :8000] importpath

Devweb runs a web server on the given address and proxies all requests
to the http server program named by importpath.  It takes care of
recompiling and restarting the program as needed.

The http server program must itself have a -addr argument that
says which TCP port to listen on.
`,
	)
}

var addr = flag.String("addr", ":8000", "web service address")
var rootPackage string

func main() {
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) != 1 {
		usage()
	}
	rootPackage = args[0]

	log.Fatal(http.ListenAndServe(*addr, http.HandlerFunc(relay)))
}

func relay(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			http.Error(w, fmt.Sprint(err), 200)
		}
	}()

	c, proxy, err := buildProxy()
	if err != nil {
		panic(err)
	}
	defer c.Close()
	_ = proxy

	outreq := new(http.Request)
	*outreq = *req // includes shallow copies of maps, but okay

	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	// Remove the connection header to the backend.  We want a
	// persistent connection, regardless of what the client sent
	// to us.  This is modifying the same underlying map from req
	// (shallow copied above) so we only copy it if necessary.
	if outreq.Header.Get("Connection") != "" {
		outreq.Header = make(http.Header)
		copyHeader(outreq.Header, req.Header)
		outreq.Header.Del("Connection")
	}

	outreq.Write(c)

	br := bufio.NewReader(c)
	resp, err := http.ReadResponse(br, outreq)
	if err != nil {
		panic(err)
	}

	copyHeader(w.Header(), resp.Header)
	w.WriteHeader(resp.StatusCode)

	if resp.Body != nil {
		io.Copy(w, resp.Body)
	}
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

type cmdProxy struct {
	cmd  *exec.Cmd
	addr string
}

func (p *cmdProxy) kill() {
	if p == nil {
		return
	}
	p.cmd.Process.Kill()
	p.cmd.Wait()
}

var proxyInfo struct {
	sync.Mutex
	build  time.Time
	check  time.Time
	active *cmdProxy
	err    error
}

func buildProxy() (c net.Conn, proxy *cmdProxy, err error) {
	p := &proxyInfo

	t := time.Now()
	p.Lock()
	defer p.Unlock()
	if t.Before(p.check) {
		// We waited for the lock while someone else dialed.
		// If we can connect, done.
		if p.active != nil {
			if c, err := net.DialTimeout("tcp", p.active.addr, 5*time.Second); err == nil {
				return c, p.active, nil
			}
		}
	}

	defer func() {
		p.err = err
		p.check = time.Now()
	}()

	pkgs, err := loadPackage(rootPackage)
	if err != nil {
		return nil, nil, fmt.Errorf("load %s: %s", rootPackage, err)
	}

	deps := pkgs[0].Deps
	if len(deps) > 0 && deps[0] == "C" {
		deps = deps[1:]
	}
	pkgs1, err := loadPackage(deps...)
	if err != nil {
		return nil, nil, fmt.Errorf("load %v: %s", deps, err)
	}
	pkgs = append(pkgs, pkgs1...)

	var latest time.Time

	for _, pkg := range pkgs {
		var files []string
		files = append(files, pkg.GoFiles...)
		files = append(files, pkg.CFiles...)
		files = append(files, pkg.HFiles...)
		files = append(files, pkg.SFiles...)
		files = append(files, pkg.CgoFiles...)

		for _, file := range files {
			if fi, err := os.Stat(filepath.Join(pkg.Dir, file)); err == nil && fi.ModTime().After(latest) {
				latest = fi.ModTime()
			}
		}
	}

	if latest.After(p.build) {
		p.active.kill()
		p.active = nil

		out, err := exec.Command("go", "build", "-o", "prox.exe", rootPackage).CombinedOutput()
		if len(out) > 0 {
			return nil, nil, fmt.Errorf("%s", out)
		}
		if err != nil {
			return nil, nil, err
		}

		p.build = latest
	}

	// If we can connect, done.
	if p.active != nil {
		if c, err := net.DialTimeout("tcp", p.active.addr, 5*time.Second); err == nil {
			return c, p.active, nil
		}
	}

	// Otherwise, start a new server.
	p.active.kill()
	p.active = nil

	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, nil, err
	}
	addr := l.Addr().String()

	cmd := exec.Command("prox.exe", "LISTEN_STDIN")
	cmd.Stdin, err = l.(*net.TCPListener).File()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err != nil {
		l.Close()
		return nil, nil, err
	}
	err = cmd.Start()
	l.Close()

	if err != nil {
		return nil, nil, err
	}

	c, err = net.DialTimeout("tcp", addr, 5*time.Second)
	if err != nil {
		return nil, nil, err
	}

	p.active = &cmdProxy{cmd, addr}
	return c, p.active, nil
}

type Pkg struct {
	ImportPath string
	Dir        string
	GoFiles    []string
	CFiles     []string
	HFiles     []string
	SFiles     []string
	CgoFiles   []string
	Deps       []string
}

func loadPackage(name ...string) ([]*Pkg, error) {
	args := []string{"list", "-json"}
	args = append(args, name...)
	var stderr bytes.Buffer
	cmd := exec.Command("go", args...)
	r, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	cmd.Stderr = &stderr
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	dec := json.NewDecoder(r)
	var pkgs []*Pkg
	for {
		p := new(Pkg)
		if err := dec.Decode(p); err != nil {
			if err == io.EOF {
				break
			}
			cmd.Process.Kill()
			return nil, err
		}
		pkgs = append(pkgs, p)
	}

	err = cmd.Wait()
	if b := stderr.Bytes(); len(b) > 0 {
		return nil, fmt.Errorf("%s", b)
	}
	if err != nil {
		return nil, err
	}
	if len(pkgs) != len(name) {
		return nil, fmt.Errorf("found fewer packages than expected")
	}
	return pkgs, nil
}
<commit_msg>devweb: gofmt<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Devweb is a simple environment for developing a web server.
// It runs its own web server on the given address and proxies
// all requests to the http server program named by importpath.
// It takes care of recompiling and restarting the program as needed.
//
// The server program should be a trivial main program like:
//
//	package main
//
//	import (
//		"code.google.com/p/rsc/devweb/slave"
//
//		_ "this/package"
//		_ "that/package"
//	)
//
//	func main() {
//		slave.Main()
//	}
//
// The import _ lines import packages that register HTTP handlers,
// like in an App Engine program.
//
// As you make changes to this/package or that/package (or their
// dependencies), devweb recompiles and relaunches the servers as
// needed to serve requests.
//
package main

// BUG(rsc): Devweb should probably

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

func usage() {
	fmt.Fprint(os.Stderr, `usage: devweb [-addr :8000] importpath

Devweb runs a web server on the given address and proxies all requests
to the http server program named by importpath.  It takes care of
recompiling and restarting the program as needed.

The http server program must itself have a -addr argument that
says which TCP port to listen on.
`,
	)
}

var addr = flag.String("addr", ":8000", "web service address")
var rootPackage string

func main() {
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) != 1 {
		usage()
	}
	rootPackage = args[0]

	log.Fatal(http.ListenAndServe(*addr, http.HandlerFunc(relay)))
}

func relay(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			http.Error(w, fmt.Sprint(err), 200)
		}
	}()

	c, proxy, err := buildProxy()
	if err != nil {
		panic(err)
	}
	defer c.Close()
	_ = proxy

	outreq := new(http.Request)
	*outreq = *req // includes shallow copies of maps, but okay

	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	// Remove the connection header to the backend.  We want a
	// persistent connection, regardless of what the client sent
	// to us.  This is modifying the same underlying map from req
	// (shallow copied above) so we only copy it if necessary.
	if outreq.Header.Get("Connection") != "" {
		outreq.Header = make(http.Header)
		copyHeader(outreq.Header, req.Header)
		outreq.Header.Del("Connection")
	}

	outreq.Write(c)

	br := bufio.NewReader(c)
	resp, err := http.ReadResponse(br, outreq)
	if err != nil {
		panic(err)
	}

	copyHeader(w.Header(), resp.Header)
	w.WriteHeader(resp.StatusCode)

	if resp.Body != nil {
		io.Copy(w, resp.Body)
	}
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

type cmdProxy struct {
	cmd  *exec.Cmd
	addr string
}

func (p *cmdProxy) kill() {
	if p == nil {
		return
	}
	p.cmd.Process.Kill()
	p.cmd.Wait()
}

var proxyInfo struct {
	sync.Mutex
	build  time.Time
	check  time.Time
	active *cmdProxy
	err    error
}

func buildProxy() (c net.Conn, proxy *cmdProxy, err error) {
	p := &proxyInfo

	t := time.Now()
	p.Lock()
	defer p.Unlock()
	if t.Before(p.check) {
		// We waited for the lock while someone else dialed.
		// If we can connect, done.
		if p.active != nil {
			if c, err := net.DialTimeout("tcp", p.active.addr, 5*time.Second); err == nil {
				return c, p.active, nil
			}
		}
	}

	defer func() {
		p.err = err
		p.check = time.Now()
	}()

	pkgs, err := loadPackage(rootPackage)
	if err != nil {
		return nil, nil, fmt.Errorf("load %s: %s", rootPackage, err)
	}

	deps := pkgs[0].Deps
	if len(deps) > 0 && deps[0] == "C" {
		deps = deps[1:]
	}
	pkgs1, err := loadPackage(deps...)
	if err != nil {
		return nil, nil, fmt.Errorf("load %v: %s", deps, err)
	}
	pkgs = append(pkgs, pkgs1...)

	var latest time.Time

	for _, pkg := range pkgs {
		var files []string
		files = append(files, pkg.GoFiles...)
		files = append(files, pkg.CFiles...)
		files = append(files, pkg.HFiles...)
		files = append(files, pkg.SFiles...)
		files = append(files, pkg.CgoFiles...)

		for _, file := range files {
			if fi, err := os.Stat(filepath.Join(pkg.Dir, file)); err == nil && fi.ModTime().After(latest) {
				latest = fi.ModTime()
			}
		}
	}

	if latest.After(p.build) {
		p.active.kill()
		p.active = nil

		out, err := exec.Command("go", "build", "-o", "prox.exe", rootPackage).CombinedOutput()
		if len(out) > 0 {
			return nil, nil, fmt.Errorf("%s", out)
		}
		if err != nil {
			return nil, nil, err
		}

		p.build = latest
	}

	// If we can connect, done.
	if p.active != nil {
		if c, err := net.DialTimeout("tcp", p.active.addr, 5*time.Second); err == nil {
			return c, p.active, nil
		}
	}

	// Otherwise, start a new server.
	p.active.kill()
	p.active = nil

	l, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, nil, err
	}
	addr := l.Addr().String()

	cmd := exec.Command("prox.exe", "LISTEN_STDIN")
	cmd.Stdin, err = l.(*net.TCPListener).File()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err != nil {
		l.Close()
		return nil, nil, err
	}
	err = cmd.Start()
	l.Close()

	if err != nil {
		return nil, nil, err
	}

	c, err = net.DialTimeout("tcp", addr, 5*time.Second)
	if err != nil {
		return nil, nil, err
	}

	p.active = &cmdProxy{cmd, addr}
	return c, p.active, nil
}

type Pkg struct {
	ImportPath string
	Dir        string
	GoFiles    []string
	CFiles     []string
	HFiles     []string
	SFiles     []string
	CgoFiles   []string
	Deps       []string
}

func loadPackage(name ...string) ([]*Pkg, error) {
	args := []string{"list", "-json"}
	args = append(args, name...)
	var stderr bytes.Buffer
	cmd := exec.Command("go", args...)
	r, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	cmd.Stderr = &stderr
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	dec := json.NewDecoder(r)
	var pkgs []*Pkg
	for {
		p := new(Pkg)
		if err := dec.Decode(p); err != nil {
			if err == io.EOF {
				break
			}
			cmd.Process.Kill()
			return nil, err
		}
		pkgs = append(pkgs, p)
	}

	err = cmd.Wait()
	if b := stderr.Bytes(); len(b) > 0 {
		return nil, fmt.Errorf("%s", b)
	}
	if err != nil {
		return nil, err
	}
	if len(pkgs) != len(name) {
		return nil, fmt.Errorf("found fewer packages than expected")
	}
	return pkgs, nil
}
<|endoftext|>


<commit_before>// +build linux

/*
   Copyright The containerd 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 tasks

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"text/tabwriter"

	wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
	v1 "github.com/containerd/cgroups/stats/v1"
	v2 "github.com/containerd/cgroups/v2/stats"
	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/typeurl"
	"github.com/urfave/cli"
)

func init() {
	// metricsCommand is only added on Linux as github.com/containerd/cgroups
	// does not compile on darwin or windows
	Command.Subcommands = append(Command.Subcommands, metricsCommand)
}

const (
	formatFlag  = "format"
	formatTable = "table"
	formatJSON  = "json"
)

var metricsCommand = cli.Command{
	Name:      "metrics",
	Usage:     "get a single data point of metrics for a task with the built-in Linux runtime",
	ArgsUsage: "CONTAINER",
	Aliases:   []string{"metric"},
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  formatFlag,
			Usage: `"table" or "json"`,
			Value: formatTable,
		},
	},
	Action: func(context *cli.Context) error {
		client, ctx, cancel, err := commands.NewClient(context)
		if err != nil {
			return err
		}
		defer cancel()
		container, err := client.LoadContainer(ctx, context.Args().First())
		if err != nil {
			return err
		}
		task, err := container.Task(ctx, nil)
		if err != nil {
			return err
		}
		metric, err := task.Metrics(ctx)
		if err != nil {
			return nil
		}
		anydata, err := typeurl.UnmarshalAny(metric.Data)
		if err != nil {
			return err
		}
		var (
			data         *v1.Metrics
			data2        *v2.Metrics
			windowsStats *wstats.Statistics
		)
		switch v := anydata.(type) {
		case *v1.Metrics:
			data = v
		case *v2.Metrics:
			data2 = v
		case *wstats.Statistics:
			windowsStats = v
		default:
			return errors.New("cannot convert metric data to cgroups.Metrics or windows.Statistics")
		}

		switch context.String(formatFlag) {
		case formatTable:
			w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0)
			fmt.Fprintf(w, "ID\tTIMESTAMP\t\n")
			fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
			if data != nil {
				printCgroupMetricsTable(w, data)
			} else if data2 != nil {
				printCgroup2MetricsTable(w, data2)
			} else {
				if windowsStats.GetLinux() != nil {
					printCgroupMetricsTable(w, windowsStats.GetLinux())
				} else if windowsStats.GetWindows() != nil {
					printWindowsContainerStatistics(w, windowsStats.GetWindows())
				}
				// Print VM stats if its isolated
				if windowsStats.VM != nil {
					printWindowsVMStatistics(w, windowsStats.VM)
				}
			}
			return w.Flush()
		case formatJSON:
			marshaledJSON, err := json.MarshalIndent(anydata, "", "  ")
			if err != nil {
				return err
			}
			fmt.Println(string(marshaledJSON))
			return nil
		default:
			return errors.New("format must be table or json")
		}
	},
}

func printCgroupMetricsTable(w *tabwriter.Writer, data *v1.Metrics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	if data.Memory != nil {
		fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage)
		fmt.Fprintf(w, "memory.limit_in_bytes\t%d\t\n", data.Memory.Usage.Limit)
		fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache)
	}
	if data.CPU != nil {
		fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total)
		fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU)
	}
	if data.Pids != nil {
		fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
		fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
	}
}

func printCgroup2MetricsTable(w *tabwriter.Writer, data *v2.Metrics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	if data.Pids != nil {
		fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
		fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
	}
	if data.CPU != nil {
		fmt.Fprintf(w, "cpu.usage_usec\t%v\t\n", data.CPU.UsageUsec)
		fmt.Fprintf(w, "cpu.user_usec\t%v\t\n", data.CPU.UserUsec)
		fmt.Fprintf(w, "cpu.sys_usec\t%v\t\n", data.CPU.SystemUsec)
		fmt.Fprintf(w, "cpu.number_of_periods\t%v\t\n", data.CPU.NrPeriods)
		fmt.Fprintf(w, "cpu.number_of_throttled\t%v\t\n", data.CPU.NrThrottled)
		fmt.Fprintf(w, "cpu.throttled_usec\t%v\t\n", data.CPU.ThrottledUsec)
	}
	if data.Memory != nil {
		fmt.Fprintf(w, "memory.usage\t%v\t\n", data.Memory.Usage)
		fmt.Fprintf(w, "memory.usage_limit\t%v\t\n", data.Memory.UsageLimit)
		fmt.Fprintf(w, "memory.swap_usage\t%v\t\n", data.Memory.SwapUsage)
		fmt.Fprintf(w, "memory.swap_limit\t%v\t\n", data.Memory.SwapLimit)
	}
}

func printWindowsContainerStatistics(w *tabwriter.Writer, stats *wstats.WindowsContainerStatistics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	fmt.Fprintf(w, "timestamp\t%s\t\n", stats.Timestamp)
	fmt.Fprintf(w, "start_time\t%s\t\n", stats.ContainerStartTime)
	fmt.Fprintf(w, "uptime_ns\t%d\t\n", stats.UptimeNS)
	if stats.Processor != nil {
		fmt.Fprintf(w, "cpu.total_runtime_ns\t%d\t\n", stats.Processor.TotalRuntimeNS)
		fmt.Fprintf(w, "cpu.runtime_user_ns\t%d\t\n", stats.Processor.RuntimeUserNS)
		fmt.Fprintf(w, "cpu.runtime_kernel_ns\t%d\t\n", stats.Processor.RuntimeUserNS)
	}
	if stats.Memory != nil {
		fmt.Fprintf(w, "memory.commit_bytes\t%d\t\n", stats.Memory.MemoryUsageCommitBytes)
		fmt.Fprintf(w, "memory.commit_peak_bytes\t%d\t\n", stats.Memory.MemoryUsageCommitPeakBytes)
		fmt.Fprintf(w, "memory.private_working_set_bytes\t%d\t\n", stats.Memory.MemoryUsagePrivateWorkingSetBytes)
	}
	if stats.Storage != nil {
		fmt.Fprintf(w, "storage.read_count_normalized\t%d\t\n", stats.Storage.ReadCountNormalized)
		fmt.Fprintf(w, "storage.read_size_bytes\t%d\t\n", stats.Storage.ReadSizeBytes)
		fmt.Fprintf(w, "storage.write_count_normalized\t%d\t\n", stats.Storage.WriteCountNormalized)
		fmt.Fprintf(w, "storage.write_size_bytes\t%d\t\n", stats.Storage.WriteSizeBytes)
	}
}

func printWindowsVMStatistics(w *tabwriter.Writer, stats *wstats.VirtualMachineStatistics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	if stats.Processor != nil {
		fmt.Fprintf(w, "vm.cpu.total_runtime_ns\t%d\t\n", stats.Processor.TotalRuntimeNS)
	}
	if stats.Memory != nil {
		fmt.Fprintf(w, "vm.memory.working_set_bytes\t%d\t\n", stats.Memory.WorkingSetBytes)
		fmt.Fprintf(w, "vm.memory.virtual_node_count\t%d\t\n", stats.Memory.VirtualNodeCount)
		fmt.Fprintf(w, "vm.memory.available\t%d\t\n", stats.Memory.VmMemory.AvailableMemory)
		fmt.Fprintf(w, "vm.memory.available_buffer\t%d\t\n", stats.Memory.VmMemory.AvailableMemoryBuffer)
		fmt.Fprintf(w, "vm.memory.reserved\t%d\t\n", stats.Memory.VmMemory.ReservedMemory)
		fmt.Fprintf(w, "vm.memory.assigned\t%d\t\n", stats.Memory.VmMemory.AssignedMemory)
		fmt.Fprintf(w, "vm.memory.slp_active\t%t\t\n", stats.Memory.VmMemory.SlpActive)
		fmt.Fprintf(w, "vm.memory.balancing_enabled\t%t\t\n", stats.Memory.VmMemory.BalancingEnabled)
		fmt.Fprintf(w, "vm.memory.dm_operation_in_progress\t%t\t\n", stats.Memory.VmMemory.DmOperationInProgress)
	}
}
<commit_msg>cpu metrics consistency<commit_after>// +build linux

/*
   Copyright The containerd 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 tasks

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"text/tabwriter"

	wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
	v1 "github.com/containerd/cgroups/stats/v1"
	v2 "github.com/containerd/cgroups/v2/stats"
	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/typeurl"
	"github.com/urfave/cli"
)

func init() {
	// metricsCommand is only added on Linux as github.com/containerd/cgroups
	// does not compile on darwin or windows
	Command.Subcommands = append(Command.Subcommands, metricsCommand)
}

const (
	formatFlag  = "format"
	formatTable = "table"
	formatJSON  = "json"
)

var metricsCommand = cli.Command{
	Name:      "metrics",
	Usage:     "get a single data point of metrics for a task with the built-in Linux runtime",
	ArgsUsage: "CONTAINER",
	Aliases:   []string{"metric"},
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  formatFlag,
			Usage: `"table" or "json"`,
			Value: formatTable,
		},
	},
	Action: func(context *cli.Context) error {
		client, ctx, cancel, err := commands.NewClient(context)
		if err != nil {
			return err
		}
		defer cancel()
		container, err := client.LoadContainer(ctx, context.Args().First())
		if err != nil {
			return err
		}
		task, err := container.Task(ctx, nil)
		if err != nil {
			return err
		}
		metric, err := task.Metrics(ctx)
		if err != nil {
			return nil
		}
		anydata, err := typeurl.UnmarshalAny(metric.Data)
		if err != nil {
			return err
		}
		var (
			data         *v1.Metrics
			data2        *v2.Metrics
			windowsStats *wstats.Statistics
		)
		switch v := anydata.(type) {
		case *v1.Metrics:
			data = v
		case *v2.Metrics:
			data2 = v
		case *wstats.Statistics:
			windowsStats = v
		default:
			return errors.New("cannot convert metric data to cgroups.Metrics or windows.Statistics")
		}

		switch context.String(formatFlag) {
		case formatTable:
			w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0)
			fmt.Fprintf(w, "ID\tTIMESTAMP\t\n")
			fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
			if data != nil {
				printCgroupMetricsTable(w, data)
			} else if data2 != nil {
				printCgroup2MetricsTable(w, data2)
			} else {
				if windowsStats.GetLinux() != nil {
					printCgroupMetricsTable(w, windowsStats.GetLinux())
				} else if windowsStats.GetWindows() != nil {
					printWindowsContainerStatistics(w, windowsStats.GetWindows())
				}
				// Print VM stats if its isolated
				if windowsStats.VM != nil {
					printWindowsVMStatistics(w, windowsStats.VM)
				}
			}
			return w.Flush()
		case formatJSON:
			marshaledJSON, err := json.MarshalIndent(anydata, "", "  ")
			if err != nil {
				return err
			}
			fmt.Println(string(marshaledJSON))
			return nil
		default:
			return errors.New("format must be table or json")
		}
	},
}

func printCgroupMetricsTable(w *tabwriter.Writer, data *v1.Metrics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	if data.Memory != nil {
		fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage)
		fmt.Fprintf(w, "memory.limit_in_bytes\t%d\t\n", data.Memory.Usage.Limit)
		fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache)
	}
	if data.CPU != nil {
		fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total)
		fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU)
	}
	if data.Pids != nil {
		fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
		fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
	}
}

func printCgroup2MetricsTable(w *tabwriter.Writer, data *v2.Metrics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	if data.Pids != nil {
		fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
		fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
	}
	if data.CPU != nil {
		fmt.Fprintf(w, "cpu.usage_usec\t%v\t\n", data.CPU.UsageUsec)
		fmt.Fprintf(w, "cpu.user_usec\t%v\t\n", data.CPU.UserUsec)
		fmt.Fprintf(w, "cpu.system_usec\t%v\t\n", data.CPU.SystemUsec)
		fmt.Fprintf(w, "cpu.nr_periods\t%v\t\n", data.CPU.NrPeriods)
		fmt.Fprintf(w, "cpu.nr_throttled\t%v\t\n", data.CPU.NrThrottled)
		fmt.Fprintf(w, "cpu.throttled_usec\t%v\t\n", data.CPU.ThrottledUsec)
	}
	if data.Memory != nil {
		fmt.Fprintf(w, "memory.usage\t%v\t\n", data.Memory.Usage)
		fmt.Fprintf(w, "memory.usage_limit\t%v\t\n", data.Memory.UsageLimit)
		fmt.Fprintf(w, "memory.swap_usage\t%v\t\n", data.Memory.SwapUsage)
		fmt.Fprintf(w, "memory.swap_limit\t%v\t\n", data.Memory.SwapLimit)
	}
}

func printWindowsContainerStatistics(w *tabwriter.Writer, stats *wstats.WindowsContainerStatistics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	fmt.Fprintf(w, "timestamp\t%s\t\n", stats.Timestamp)
	fmt.Fprintf(w, "start_time\t%s\t\n", stats.ContainerStartTime)
	fmt.Fprintf(w, "uptime_ns\t%d\t\n", stats.UptimeNS)
	if stats.Processor != nil {
		fmt.Fprintf(w, "cpu.total_runtime_ns\t%d\t\n", stats.Processor.TotalRuntimeNS)
		fmt.Fprintf(w, "cpu.runtime_user_ns\t%d\t\n", stats.Processor.RuntimeUserNS)
		fmt.Fprintf(w, "cpu.runtime_kernel_ns\t%d\t\n", stats.Processor.RuntimeUserNS)
	}
	if stats.Memory != nil {
		fmt.Fprintf(w, "memory.commit_bytes\t%d\t\n", stats.Memory.MemoryUsageCommitBytes)
		fmt.Fprintf(w, "memory.commit_peak_bytes\t%d\t\n", stats.Memory.MemoryUsageCommitPeakBytes)
		fmt.Fprintf(w, "memory.private_working_set_bytes\t%d\t\n", stats.Memory.MemoryUsagePrivateWorkingSetBytes)
	}
	if stats.Storage != nil {
		fmt.Fprintf(w, "storage.read_count_normalized\t%d\t\n", stats.Storage.ReadCountNormalized)
		fmt.Fprintf(w, "storage.read_size_bytes\t%d\t\n", stats.Storage.ReadSizeBytes)
		fmt.Fprintf(w, "storage.write_count_normalized\t%d\t\n", stats.Storage.WriteCountNormalized)
		fmt.Fprintf(w, "storage.write_size_bytes\t%d\t\n", stats.Storage.WriteSizeBytes)
	}
}

func printWindowsVMStatistics(w *tabwriter.Writer, stats *wstats.VirtualMachineStatistics) {
	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
	if stats.Processor != nil {
		fmt.Fprintf(w, "vm.cpu.total_runtime_ns\t%d\t\n", stats.Processor.TotalRuntimeNS)
	}
	if stats.Memory != nil {
		fmt.Fprintf(w, "vm.memory.working_set_bytes\t%d\t\n", stats.Memory.WorkingSetBytes)
		fmt.Fprintf(w, "vm.memory.virtual_node_count\t%d\t\n", stats.Memory.VirtualNodeCount)
		fmt.Fprintf(w, "vm.memory.available\t%d\t\n", stats.Memory.VmMemory.AvailableMemory)
		fmt.Fprintf(w, "vm.memory.available_buffer\t%d\t\n", stats.Memory.VmMemory.AvailableMemoryBuffer)
		fmt.Fprintf(w, "vm.memory.reserved\t%d\t\n", stats.Memory.VmMemory.ReservedMemory)
		fmt.Fprintf(w, "vm.memory.assigned\t%d\t\n", stats.Memory.VmMemory.AssignedMemory)
		fmt.Fprintf(w, "vm.memory.slp_active\t%t\t\n", stats.Memory.VmMemory.SlpActive)
		fmt.Fprintf(w, "vm.memory.balancing_enabled\t%t\t\n", stats.Memory.VmMemory.BalancingEnabled)
		fmt.Fprintf(w, "vm.memory.dm_operation_in_progress\t%t\t\n", stats.Memory.VmMemory.DmOperationInProgress)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// TODO(dimitern): bug http://pad.lv/1425569
// Disabled until we have time to fix these tests on i386 properly.
//
// +build !386

package commands

import (
	"flag"
	stdtesting "testing"

	cmdtesting "github.com/juju/juju/cmd/testing"
	_ "github.com/juju/juju/provider/dummy"
	"github.com/juju/juju/testing"
)

func TestPackage(t *stdtesting.T) {
	testing.MgoTestPackage(t)
}

// Reentrancy point for testing (something as close as possible to) the juju
// tool itself.
func TestRunMain(t *stdtesting.T) {
	if *cmdtesting.FlagRunMain {
		Main(flag.Args())
	}
}
<commit_msg>cmd/juju/commands: skip package under -race<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"flag"
	"runtime"
	stdtesting "testing"

	gittesting "github.com/juju/testing"

	cmdtesting "github.com/juju/juju/cmd/testing"
	_ "github.com/juju/juju/provider/dummy"
	"github.com/juju/juju/testing"
)

func TestPackage(t *stdtesting.T) {
	if runtime.GOARCH == "386" {
		t.Skipf("skipping package for %v/%v, see http://pad.lv/1425569", runtime.GOOS, runtime.GOARCH)
	}
	if gittesting.RaceEnabled {
		t.Skip("skipping test in -race mode, see LP 1518810")
	}
	testing.MgoTestPackage(t)
}

// Reentrancy point for testing (something as close as possible to) the juju
// tool itself.
func TestRunMain(t *stdtesting.T) {
	if *cmdtesting.FlagRunMain {
		Main(flag.Args())
	}
}
<|endoftext|>


<commit_before>package redis

import (
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/funkygao/Go-Redis"
	"github.com/funkygao/gafka/cmd/kguard/monitor"
	"github.com/funkygao/gafka/telemetry"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	log "github.com/funkygao/log4go"
)

func init() {
	monitor.RegisterWatcher("redis.info", func() monitor.Watcher {
		return &WatchRedisInfo{
			Tick: time.Minute,
		}
	})
}

// WatchRedisInfo watches registered redis instances with redis 'info' command.
type WatchRedisInfo struct {
	Zkzone *zk.ZkZone
	Stop   <-chan struct{}
	Tick   time.Duration
	Wg     *sync.WaitGroup

	deadInstance, syncPartial metrics.Counter
	conns                     map[string]metrics.Gauge
	blocked                   map[string]metrics.Gauge
	usedMem                   map[string]metrics.Gauge
	ops                       map[string]metrics.Gauge
	rejected                  map[string]metrics.Gauge
	rxKbps                    map[string]metrics.Gauge
	txKbps                    map[string]metrics.Gauge
	expiredKeys               map[string]metrics.Gauge
	keys                      map[string]metrics.Gauge
}

func (this *WatchRedisInfo) Init(ctx monitor.Context) {
	this.Zkzone = ctx.ZkZone()
	this.Stop = ctx.StopChan()
	this.Wg = ctx.Inflight()
}

func (this *WatchRedisInfo) Run() {
	defer this.Wg.Done()

	ticker := time.NewTicker(this.Tick)
	defer ticker.Stop()

	this.deadInstance = metrics.NewRegisteredCounter("redis.dead", nil)
	this.syncPartial = metrics.NewRegisteredCounter("redis.sync.partial", nil)
	this.conns = make(map[string]metrics.Gauge, 10)
	this.blocked = make(map[string]metrics.Gauge, 10)
	this.usedMem = make(map[string]metrics.Gauge, 10)
	this.ops = make(map[string]metrics.Gauge, 10)
	this.rejected = make(map[string]metrics.Gauge, 10)
	this.rxKbps = make(map[string]metrics.Gauge, 10)
	this.txKbps = make(map[string]metrics.Gauge, 10)
	this.expiredKeys = make(map[string]metrics.Gauge, 10)
	this.keys = make(map[string]metrics.Gauge, 10)

	for {
		select {
		case <-this.Stop:
			log.Info("redis.info stopped")
			return

		case <-ticker.C:
			var wg sync.WaitGroup
			for _, hostPort := range this.Zkzone.AllRedis() {
				host, port, err := net.SplitHostPort(hostPort)
				if err != nil {
					log.Error("invalid redis instance: %s", hostPort)
					continue
				}

				nport, err := strconv.Atoi(port)
				if err != nil || nport < 0 {
					log.Error("invalid redis instance: %s", hostPort)
					continue
				}

				// TODO ver=role(master|slave)
				tag := telemetry.Tag(strings.Replace(host, ".", "_", -1), port, "v1")
				if _, present := this.conns[tag]; !present {
					this.conns[tag] = metrics.NewRegisteredGauge(tag+"redis.conns", nil)              // connected_clients
					this.blocked[tag] = metrics.NewRegisteredGauge(tag+"redis.blocked", nil)          // blocked_clients
					this.usedMem[tag] = metrics.NewRegisteredGauge(tag+"redis.mem.used", nil)         // used_memory
					this.ops[tag] = metrics.NewRegisteredGauge(tag+"redis.ops", nil)                  // instantaneous_ops_per_sec
					this.rejected[tag] = metrics.NewRegisteredGauge(tag+"redis.rejected", nil)        // rejected_connections
					this.rxKbps[tag] = metrics.NewRegisteredGauge(tag+"redis.rx.kbps", nil)           // instantaneous_input_kbps
					this.txKbps[tag] = metrics.NewRegisteredGauge(tag+"redis.tx.kbps", nil)           // instantaneous_output_kbps
					this.expiredKeys[tag] = metrics.NewRegisteredGauge(tag+"redis.expired.keys", nil) // expired_keys
					this.keys[tag] = metrics.NewRegisteredGauge(tag+"redis.keys", nil)                // db0:keys=15500,expires=15500,avg_ttl=27438570
				}

				wg.Add(1)
				go this.updateRedisInfo(&wg, host, nport, tag)
			}

			wg.Wait()

		}
	}
}

func (this *WatchRedisInfo) updateRedisInfo(wg *sync.WaitGroup, host string, port int, tag string) {
	defer wg.Done()

	spec := redis.DefaultSpec().Host(host).Port(port)
	client, err := redis.NewSynchClientWithSpec(spec)
	if err != nil {
		log.Error("redis[%s:%d]: %v", host, port, err)
		this.deadInstance.Inc(1)
		return
	}
	defer client.Quit()

	infoMap, err := client.Info()
	if err != nil {
		log.Error("redis[%s:%d] info: %v", host, port, err)
		this.deadInstance.Inc(1)
		return
	}

	var keysN int64
	// db0:keys=15500,expires=15500,avg_ttl=27438570
	for key, value := range infoMap {
		if strings.HasPrefix(key, "db") && strings.Contains(value, "keys=") {
			keysN += extractKeysCount(value)
		}
	}

	this.keys[tag].Update(keysN)

	conns, _ := strconv.ParseInt(infoMap["connected_clients"], 10, 64)
	blocked, _ := strconv.ParseInt(infoMap["blocked_clients"], 10, 64)
	mem, _ := strconv.ParseInt(infoMap["used_memory"], 10, 64)
	ops, _ := strconv.ParseInt(infoMap["instantaneous_ops_per_sec"], 10, 64)
	rejected, _ := strconv.ParseInt(infoMap["rejected_connections"], 10, 64)
	syncPartial, _ := strconv.ParseInt(infoMap["sync_partial_err"], 10, 64)
	rxKbps, _ := strconv.ParseFloat(infoMap["instantaneous_input_kbps"], 64)
	txKbps, _ := strconv.ParseFloat(infoMap["instantaneous_output_kbps"], 64)
	expiredKeys, _ := strconv.ParseInt(infoMap["expired_keys"], 10, 64)

	this.syncPartial.Inc(syncPartial)

	this.conns[tag].Update(conns)
	this.blocked[tag].Update(blocked)
	this.usedMem[tag].Update(mem)
	this.ops[tag].Update(ops)
	this.rejected[tag].Update(rejected)
	this.rxKbps[tag].Update(int64(rxKbps))
	this.txKbps[tag].Update(int64(txKbps))
	this.expiredKeys[tag].Update(expiredKeys)
}

func extractKeysCount(info string) (n int64) {
	parts := strings.SplitN(info, "keys=", 2)
	if len(parts) != 2 {
		return
	}

	p := strings.SplitN(parts[1], ",", 2)
	if len(p) != 2 {
		return
	}

	n, _ = strconv.ParseInt(p[0], 10, 64)
	return

}
<commit_msg>record instances of redis<commit_after>package redis

import (
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/funkygao/Go-Redis"
	"github.com/funkygao/gafka/cmd/kguard/monitor"
	"github.com/funkygao/gafka/telemetry"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	log "github.com/funkygao/log4go"
)

func init() {
	monitor.RegisterWatcher("redis.info", func() monitor.Watcher {
		return &WatchRedisInfo{
			Tick: time.Minute,
		}
	})
}

// WatchRedisInfo watches registered redis instances with redis 'info' command.
type WatchRedisInfo struct {
	Zkzone *zk.ZkZone
	Stop   <-chan struct{}
	Tick   time.Duration
	Wg     *sync.WaitGroup

	deadInstance, syncPartial metrics.Counter
	instances                 metrics.Gauge
	conns                     map[string]metrics.Gauge
	blocked                   map[string]metrics.Gauge
	usedMem                   map[string]metrics.Gauge
	ops                       map[string]metrics.Gauge
	rejected                  map[string]metrics.Gauge
	rxKbps                    map[string]metrics.Gauge
	txKbps                    map[string]metrics.Gauge
	expiredKeys               map[string]metrics.Gauge
	keys                      map[string]metrics.Gauge
}

func (this *WatchRedisInfo) Init(ctx monitor.Context) {
	this.Zkzone = ctx.ZkZone()
	this.Stop = ctx.StopChan()
	this.Wg = ctx.Inflight()
}

func (this *WatchRedisInfo) Run() {
	defer this.Wg.Done()

	ticker := time.NewTicker(this.Tick)
	defer ticker.Stop()

	this.instances = metrics.NewRegisteredGauge("redis.n", nil)
	this.deadInstance = metrics.NewRegisteredCounter("redis.dead", nil)
	this.syncPartial = metrics.NewRegisteredCounter("redis.sync.partial", nil)

	this.conns = make(map[string]metrics.Gauge, 10)
	this.blocked = make(map[string]metrics.Gauge, 10)
	this.usedMem = make(map[string]metrics.Gauge, 10)
	this.ops = make(map[string]metrics.Gauge, 10)
	this.rejected = make(map[string]metrics.Gauge, 10)
	this.rxKbps = make(map[string]metrics.Gauge, 10)
	this.txKbps = make(map[string]metrics.Gauge, 10)
	this.expiredKeys = make(map[string]metrics.Gauge, 10)
	this.keys = make(map[string]metrics.Gauge, 10)

	for {
		select {
		case <-this.Stop:
			log.Info("redis.info stopped")
			return

		case <-ticker.C:
			var (
				wg     sync.WaitGroup
				redisN int64
			)

			for _, hostPort := range this.Zkzone.AllRedis() {
				host, port, err := net.SplitHostPort(hostPort)
				if err != nil {
					log.Error("invalid redis instance: %s", hostPort)
					continue
				}

				nport, err := strconv.Atoi(port)
				if err != nil || nport < 0 {
					log.Error("invalid redis instance: %s", hostPort)
					continue
				}

				redisN++

				// TODO ver=role(master|slave)
				tag := telemetry.Tag(strings.Replace(host, ".", "_", -1), port, "v1")
				if _, present := this.conns[tag]; !present {
					this.conns[tag] = metrics.NewRegisteredGauge(tag+"redis.conns", nil)              // connected_clients
					this.blocked[tag] = metrics.NewRegisteredGauge(tag+"redis.blocked", nil)          // blocked_clients
					this.usedMem[tag] = metrics.NewRegisteredGauge(tag+"redis.mem.used", nil)         // used_memory
					this.ops[tag] = metrics.NewRegisteredGauge(tag+"redis.ops", nil)                  // instantaneous_ops_per_sec
					this.rejected[tag] = metrics.NewRegisteredGauge(tag+"redis.rejected", nil)        // rejected_connections
					this.rxKbps[tag] = metrics.NewRegisteredGauge(tag+"redis.rx.kbps", nil)           // instantaneous_input_kbps
					this.txKbps[tag] = metrics.NewRegisteredGauge(tag+"redis.tx.kbps", nil)           // instantaneous_output_kbps
					this.expiredKeys[tag] = metrics.NewRegisteredGauge(tag+"redis.expired.keys", nil) // expired_keys
					this.keys[tag] = metrics.NewRegisteredGauge(tag+"redis.keys", nil)                // db0:keys=15500,expires=15500,avg_ttl=27438570
				}

				wg.Add(1)
				go this.updateRedisInfo(&wg, host, nport, tag)
			}

			this.instances.Update(redisN)
			wg.Wait()

		}
	}
}

func (this *WatchRedisInfo) updateRedisInfo(wg *sync.WaitGroup, host string, port int, tag string) {
	defer wg.Done()

	spec := redis.DefaultSpec().Host(host).Port(port)
	client, err := redis.NewSynchClientWithSpec(spec)
	if err != nil {
		log.Error("redis[%s:%d]: %v", host, port, err)
		this.deadInstance.Inc(1)
		return
	}
	defer client.Quit()

	infoMap, err := client.Info()
	if err != nil {
		log.Error("redis[%s:%d] info: %v", host, port, err)
		this.deadInstance.Inc(1)
		return
	}

	var keysN int64
	// db0:keys=15500,expires=15500,avg_ttl=27438570
	for key, value := range infoMap {
		if strings.HasPrefix(key, "db") && strings.Contains(value, "keys=") {
			keysN += extractKeysCount(value)
		}
	}

	this.keys[tag].Update(keysN)

	conns, _ := strconv.ParseInt(infoMap["connected_clients"], 10, 64)
	blocked, _ := strconv.ParseInt(infoMap["blocked_clients"], 10, 64)
	mem, _ := strconv.ParseInt(infoMap["used_memory"], 10, 64)
	ops, _ := strconv.ParseInt(infoMap["instantaneous_ops_per_sec"], 10, 64)
	rejected, _ := strconv.ParseInt(infoMap["rejected_connections"], 10, 64)
	syncPartial, _ := strconv.ParseInt(infoMap["sync_partial_err"], 10, 64)
	rxKbps, _ := strconv.ParseFloat(infoMap["instantaneous_input_kbps"], 64)
	txKbps, _ := strconv.ParseFloat(infoMap["instantaneous_output_kbps"], 64)
	expiredKeys, _ := strconv.ParseInt(infoMap["expired_keys"], 10, 64)

	this.syncPartial.Inc(syncPartial)

	this.conns[tag].Update(conns)
	this.blocked[tag].Update(blocked)
	this.usedMem[tag].Update(mem)
	this.ops[tag].Update(ops)
	this.rejected[tag].Update(rejected)
	this.rxKbps[tag].Update(int64(rxKbps))
	this.txKbps[tag].Update(int64(txKbps))
	this.expiredKeys[tag].Update(expiredKeys)
}

func extractKeysCount(info string) (n int64) {
	parts := strings.SplitN(info, "keys=", 2)
	if len(parts) != 2 {
		return
	}

	p := strings.SplitN(parts[1], ",", 2)
	if len(p) != 2 {
		return
	}

	n, _ = strconv.ParseInt(p[0], 10, 64)
	return

}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
)

// Bootstrapper is the name for bootstrapper
const Bootstrapper = "bootstrapper"

type setFn func(string, string) error

// Setting represents a setting
type Setting struct {
	name        string
	set         func(config.MinikubeConfig, string, string) error
	setMap      func(config.MinikubeConfig, string, map[string]interface{}) error
	validations []setFn
	callbacks   []setFn
}

// These are all the settings that are configurable
// and their validation and callback fn run on Set
var settings = []Setting{
	{
		name:        "vm-driver",
		set:         SetString,
		validations: []setFn{IsValidDriver},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:      "feature-gates",
		set:       SetString,
		callbacks: []setFn{RequiresRestartMsg},
	},
	{
		name:        "v",
		set:         SetInt,
		validations: []setFn{IsPositive},
	},
	{
		name:        "cpus",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "disk-size",
		set:         SetString,
		validations: []setFn{IsValidDiskSize},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "host-only-cidr",
		set:         SetString,
		validations: []setFn{IsValidCIDR},
	},
	{
		name:        "memory",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "log_dir",
		set:         SetString,
		validations: []setFn{IsValidPath},
	},
	{
		name: "kubernetes-version",
		set:  SetString,
	},
	{
		name:        "iso-url",
		set:         SetString,
		validations: []setFn{IsValidURL, IsURLExists},
	},
	{
		name: config.WantUpdateNotification,
		set:  SetBool,
	},
	{
		name: config.ReminderWaitPeriodInHours,
		set:  SetInt,
	},
	{
		name: config.WantReportError,
		set:  SetBool,
	},
	{
		name: config.WantReportErrorPrompt,
		set:  SetBool,
	},
	{
		name: config.WantKubectlDownloadMsg,
		set:  SetBool,
	},
	{
		name: config.WantNoneDriverWarning,
		set:  SetBool,
	},
	{
		name: config.MachineProfile,
		set:  SetString,
	},
	{
		name: Bootstrapper,
		set:  SetString, //TODO(r2d4): more validation here?
	},
	{
		name: config.ShowDriverDeprecationNotification,
		set:  SetBool,
	},
	{
		name: config.ShowBootstrapperDeprecationNotification,
		set:  SetBool,
	},
	{
		name:        "dashboard",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "addon-manager",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "heapster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "efk",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "ingress",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry-creds",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "freshpod",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "storage-provisioner",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "storage-provisioner-gluster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "metrics-server",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-driver-installer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-gpu-device-plugin",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "logviewer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
	},
	{
		name:        "gvisor",
		set:         SetBool,
		validations: []setFn{IsValidAddon, IsContainerdRuntime},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name: "hyperv-virtual-switch",
		set:  SetString,
	},
	{
		name: "disable-driver-mounts",
		set:  SetBool,
	},
	{
		name:   "cache",
		set:    SetConfigMap,
		setMap: SetMap,
	},
	{
		name: "embed-certs",
		set:  SetBool,
	},
}

// ConfigCmd represents the config command
var ConfigCmd = &cobra.Command{
	Use:   "config SUBCOMMAND [flags]",
	Short: "Modify minikube config",
	Long: `config modifies minikube config files using subcommands like "minikube config set vm-driver kvm"
Configurable fields: ` + "\n\n" + configurableFields(),
	Run: func(cmd *cobra.Command, args []string) {
		if err := cmd.Help(); err != nil {
			glog.Errorf("help: %v", err)
		}
	},
}

func configurableFields() string {
	fields := []string{}
	for _, s := range settings {
		fields = append(fields, " * "+s.name)
	}
	return strings.Join(fields, "\n")
}

// ListConfigMap list entries from config file
func ListConfigMap(name string) ([]string, error) {
	configFile, err := config.ReadConfig()
	if err != nil {
		return nil, err
	}
	var images []string
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			images = append(images, key)
		}
	}
	return images, nil
}

// AddToConfigMap adds entries to a map in the config file
func AddToConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	newImages := make(map[string]interface{})
	for _, image := range images {
		newImages[image] = nil
	}
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			newImages[key] = nil
		}
	}
	if err = s.setMap(configFile, name, newImages); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// DeleteFromConfigMap deletes entries from a map in the config file
func DeleteFromConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	values, ok := configFile[name]
	if !ok {
		return nil
	}
	for _, image := range images {
		delete(values.(map[string]interface{}), image)
	}
	if err = s.setMap(configFile, name, values.(map[string]interface{})); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// WriteConfig writes a minikube config to the JSON file
func WriteConfig(m config.MinikubeConfig) error {
	f, err := os.Create(constants.ConfigFile)
	if err != nil {
		return fmt.Errorf("create %s: %s", constants.ConfigFile, err)
	}
	defer f.Close()
	err = encode(f, m)
	if err != nil {
		return fmt.Errorf("encode %s: %s", constants.ConfigFile, err)
	}
	return nil
}

func encode(w io.Writer, m config.MinikubeConfig) error {
	b, err := json.MarshalIndent(m, "", "    ")
	if err != nil {
		return err
	}

	_, err = w.Write(b)

	return err
}
<commit_msg>[FEATURE] allow to set container-runtime via config<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
)

// Bootstrapper is the name for bootstrapper
const Bootstrapper = "bootstrapper"

type setFn func(string, string) error

// Setting represents a setting
type Setting struct {
	name        string
	set         func(config.MinikubeConfig, string, string) error
	setMap      func(config.MinikubeConfig, string, map[string]interface{}) error
	validations []setFn
	callbacks   []setFn
}

// These are all the settings that are configurable
// and their validation and callback fn run on Set
var settings = []Setting{
	{
		name:        "vm-driver",
		set:         SetString,
		validations: []setFn{IsValidDriver},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:      "feature-gates",
		set:       SetString,
		callbacks: []setFn{RequiresRestartMsg},
	},
	{
		name:        "v",
		set:         SetInt,
		validations: []setFn{IsPositive},
	},
	{
		name:        "cpus",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "disk-size",
		set:         SetString,
		validations: []setFn{IsValidDiskSize},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "host-only-cidr",
		set:         SetString,
		validations: []setFn{IsValidCIDR},
	},
	{
		name:        "memory",
		set:         SetInt,
		validations: []setFn{IsPositive},
		callbacks:   []setFn{RequiresRestartMsg},
	},
	{
		name:        "log_dir",
		set:         SetString,
		validations: []setFn{IsValidPath},
	},
	{
		name: "kubernetes-version",
		set:  SetString,
	},
	{
		name:        "iso-url",
		set:         SetString,
		validations: []setFn{IsValidURL, IsURLExists},
	},
	{
		name: config.WantUpdateNotification,
		set:  SetBool,
	},
	{
		name: config.ReminderWaitPeriodInHours,
		set:  SetInt,
	},
	{
		name: config.WantReportError,
		set:  SetBool,
	},
	{
		name: config.WantReportErrorPrompt,
		set:  SetBool,
	},
	{
		name: config.WantKubectlDownloadMsg,
		set:  SetBool,
	},
	{
		name: config.WantNoneDriverWarning,
		set:  SetBool,
	},
	{
		name: config.MachineProfile,
		set:  SetString,
	},
	{
		name: Bootstrapper,
		set:  SetString, //TODO(r2d4): more validation here?
	},
	{
		name: config.ShowDriverDeprecationNotification,
		set:  SetBool,
	},
	{
		name: config.ShowBootstrapperDeprecationNotification,
		set:  SetBool,
	},
	{
		name:        "dashboard",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "addon-manager",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "heapster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "efk",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "ingress",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "insecure-registry",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "registry-creds",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "freshpod",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "default-storageclass",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "storage-provisioner",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "storage-provisioner-gluster",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableStorageClasses},
	},
	{
		name:        "metrics-server",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-driver-installer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "nvidia-gpu-device-plugin",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name:        "logviewer",
		set:         SetBool,
		validations: []setFn{IsValidAddon},
	},
	{
		name:        "gvisor",
		set:         SetBool,
		validations: []setFn{IsValidAddon, IsContainerdRuntime},
		callbacks:   []setFn{EnableOrDisableAddon},
	},
	{
		name: "hyperv-virtual-switch",
		set:  SetString,
	},
	{
		name: "disable-driver-mounts",
		set:  SetBool,
	},
	{
		name:   "cache",
		set:    SetConfigMap,
		setMap: SetMap,
	},
	{
		name: "embed-certs",
		set:  SetBool,
	},
}

// ConfigCmd represents the config command
var ConfigCmd = &cobra.Command{
	Use:   "config SUBCOMMAND [flags]",
	Short: "Modify minikube config",
	Long: `config modifies minikube config files using subcommands like "minikube config set vm-driver kvm"
Configurable fields: ` + "\n\n" + configurableFields(),
	Run: func(cmd *cobra.Command, args []string) {
		if err := cmd.Help(); err != nil {
			glog.Errorf("help: %v", err)
		}
	},
}

func configurableFields() string {
	fields := []string{}
	for _, s := range settings {
		fields = append(fields, " * "+s.name)
	}
	return strings.Join(fields, "\n")
}

// ListConfigMap list entries from config file
func ListConfigMap(name string) ([]string, error) {
	configFile, err := config.ReadConfig()
	if err != nil {
		return nil, err
	}
	var images []string
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			images = append(images, key)
		}
	}
	return images, nil
}

// AddToConfigMap adds entries to a map in the config file
func AddToConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	newImages := make(map[string]interface{})
	for _, image := range images {
		newImages[image] = nil
	}
	if values, ok := configFile[name].(map[string]interface{}); ok {
		for key := range values {
			newImages[key] = nil
		}
	}
	if err = s.setMap(configFile, name, newImages); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// DeleteFromConfigMap deletes entries from a map in the config file
func DeleteFromConfigMap(name string, images []string) error {
	s, err := findSetting(name)
	if err != nil {
		return err
	}
	// Set the values
	configFile, err := config.ReadConfig()
	if err != nil {
		return err
	}
	values, ok := configFile[name]
	if !ok {
		return nil
	}
	for _, image := range images {
		delete(values.(map[string]interface{}), image)
	}
	if err = s.setMap(configFile, name, values.(map[string]interface{})); err != nil {
		return err
	}
	// Write the values
	return WriteConfig(configFile)
}

// WriteConfig writes a minikube config to the JSON file
func WriteConfig(m config.MinikubeConfig) error {
	f, err := os.Create(constants.ConfigFile)
	if err != nil {
		return fmt.Errorf("create %s: %s", constants.ConfigFile, err)
	}
	defer f.Close()
	err = encode(f, m)
	if err != nil {
		return fmt.Errorf("encode %s: %s", constants.ConfigFile, err)
	}
	return nil
}

func encode(w io.Writer, m config.MinikubeConfig) error {
	b, err := json.MarshalIndent(m, "", "    ")
	if err != nil {
		return err
	}

	_, err = w.Write(b)

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/griffithsh/sql-squish/database"
)

func outputStdout(d database.Database) error {
	files, err := d.SortedTables()
	if err != nil {
		return err
	}
	for _, file := range files {
		fmt.Print(file)
	}
	return nil
}
<commit_msg>Reuse Database String() for Stdout mode<commit_after>package main

import (
	"fmt"

	"github.com/griffithsh/sql-squish/database"
)

func outputStdout(d database.Database) error {
	fmt.Println(d.String())
	return nil
}
<|endoftext|>


<commit_before>package golisp

import "testing"

var testdata = []struct {
	expression string
	result     string
}{
	{"(1 2 3)", "(1 2 3)"},
	{"(+ 2 3)", "5"},
	{"(+ 1 6)", "7"},
	{"(oddp (+ 1 6))", "t"},
	{"(* 3 (+ 1 6))", "21"},
	{"(/ (* 2 11) (+ 1 6))", "22/7"},
}

func TestGolisp(t *testing.T) {
	for _, test := range testdata {
		e := Parse(test.expression)
		r := Eval(e.(List))
		if r.str() != test.result {
			t.Errorf("%v did not lead to %v, it lead to %v", test.expression, test.result, r.str())
		}
	}
}
<commit_msg>Added 6, 7 and 8 to the mix. This closes #15.<commit_after>package golisp

import "testing"

var testdata = []struct {
	expression string
	result     string
}{
	{"(1 2 3)", "(1 2 3)"},
	{"(+ 2 3)", "5"},
	{"(+ 1 6)", "7"},
	{"(oddp (+ 1 6))", "t"},
	{"(* 3 (+ 1 6))", "21"},
	{"(/ (* 2 11) (+ 1 6))", "22/7"},
	{"23", "23"},
	{"t", "t"},
	{"nil", "nil"},
}

func TestGolisp(t *testing.T) {
	for _, test := range testdata {
		e := Parse(test.expression)
		r := Eval(e.(Primitive))
		if r.str() != test.result {
			t.Errorf("%v did not lead to %v, it lead to %v", test.expression, test.result, r.str())
		}
	}
}
<|endoftext|>