<commit_before>package cmd

import (
	"fmt"
	"os"
	"path"

	"github.com/deifyed/water/cmd/water"
	"github.com/deifyed/water/pkg/config"
	"github.com/spf13/afero"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cfgFile      string
	logLevel     string
	templatesDir string

	fs = &afero.Afero{Fs: afero.NewOsFs()}
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:          "water",
	Short:        "Scaffold files and directories based on names and context",
	Example:      "water Makefile",
	SilenceUsage: true,
	Args:         cobra.ExactArgs(1),
	RunE:         water.RunE(fs),
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	home, err := os.UserHomeDir()
	if err != nil {
		cobra.CheckErr(err)
	}

	cfgDir := path.Join(home, ".config", "water")
	templatesDir = path.Join(cfgDir, "templates")

	rootCmd.PersistentFlags().StringVar(
		&cfgFile,
		"config",
		"",
		"config file (default $HOME/.config/water/water.yaml)",
	)

	rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "info", "log level")

	err = viper.BindPFlag(config.LogLevel, rootCmd.PersistentFlags().Lookup("log-level"))
	cobra.CheckErr(err)

	rootCmd.Flags().StringVarP(&templatesDir, "templates", "t", templatesDir, "templates directory")

	err = viper.BindPFlag(config.TemplatesDirectory, rootCmd.Flags().Lookup("templates"))
	cobra.CheckErr(err)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Find home directory.
		home, err := os.UserHomeDir()
		cobra.CheckErr(err)

		cfgDir := path.Join(home, ".config", "water")

		viper.AddConfigPath(home)
		viper.AddConfigPath(cfgDir)
		viper.SetConfigType("yaml")
		viper.SetConfigName("water")
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
	} else {
		fmt.Fprintln(os.Stderr, "No config file found, using defaults")
	}
}
<commit_msg>👌 Improve flags language<commit_after>package cmd

import (
	"fmt"
	"os"
	"path"

	"github.com/deifyed/water/cmd/water"
	"github.com/deifyed/water/pkg/config"
	"github.com/spf13/afero"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cfgFile      string
	logLevel     string
	templatesDir string

	fs = &afero.Afero{Fs: afero.NewOsFs()}
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:          "water",
	Short:        "Scaffold files and directories based on names and context",
	Example:      "water Makefile",
	SilenceUsage: true,
	Args:         cobra.ExactArgs(1),
	RunE:         water.RunE(fs),
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	home, err := os.UserHomeDir()
	if err != nil {
		cobra.CheckErr(err)
	}

	cfgDir := path.Join(home, ".config", "water")
	templatesDir = path.Join(cfgDir, "templates")

	rootCmd.PersistentFlags().StringVar(
		&cfgFile,
		"config",
		"",
		"set config file (default $HOME/.config/water/water.yaml)",
	)

	rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "info", "set log level")

	err = viper.BindPFlag(config.LogLevel, rootCmd.PersistentFlags().Lookup("log-level"))
	cobra.CheckErr(err)

	rootCmd.Flags().StringVarP(&templatesDir, "templates", "t", templatesDir, "set templates directory")

	err = viper.BindPFlag(config.TemplatesDirectory, rootCmd.Flags().Lookup("templates"))
	cobra.CheckErr(err)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Find home directory.
		home, err := os.UserHomeDir()
		cobra.CheckErr(err)

		cfgDir := path.Join(home, ".config", "water")

		viper.AddConfigPath(home)
		viper.AddConfigPath(cfgDir)
		viper.SetConfigType("yaml")
		viper.SetConfigName("water")
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
	} else {
		fmt.Fprintln(os.Stderr, "No config file found, using defaults")
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"fmt"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const rKey string = "rancherKey"
const rSecret string = "rancherSecret"
const rURL string = "rancherURL"

var cfgFile string

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "ranchhand",
	Short: "Ranch hand helps out on the ranch",
	Long:  `Ranch hand is a set of cli tools for supporting a rancher cluster`,
	// Uncomment the following line if your bare application
	// has an action associated with it:
	//Run: func(cmd *cobra.Command, args []string) { }
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	// Here you will define your flags and configuration settings.
	// Cobra supports Persistent Flags, which, if defined here,
	// will be global for your application.
	RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ranchhand.yml)")
	RootCmd.PersistentFlags().String(rKey, "", "Rancher API key")
	RootCmd.PersistentFlags().String(rSecret, "", "Rancher API secret")
	RootCmd.PersistentFlags().String(rURL, "", "Rancher API url")
	viper.BindPFlags(RootCmd.PersistentFlags())
	//viper.BindPFlag("rancherKey", RootCmd.Flags().Lookup("rancherKey"))
	//viper.BindPFlag("rancherSecret", RootCmd.Flags().Lookup("rancherSecret"))
	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	// RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}
	viper.SetConfigType("yaml")
	viper.SetConfigName("ranchhand")
	viper.AddConfigPath("/etc/ranchhand/")
	viper.AddConfigPath("$HOME/.ranchhand")
	viper.AddConfigPath("./")

	viper.BindEnv(rKey, "RANCHER_KEY") // need to bind name key to env KEY
	viper.BindEnv(rSecret, "RANCHER_SECRET")
	viper.BindEnv(rURL, "RANCHER_URL")
	viper.BindEnv("logginglevel", "LOGGING_LEVEL")

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err != nil {
		fmt.Println("Error reading config")
	}

	level, err := log.ParseLevel(strings.ToLower(viper.GetString("loggingLevel")))
	if err != nil {
		log.Error("Invalid logging level")
	} else {
		log.SetLevel(level)
		log.Debug("Logging level: ", level)
		log.Debug("Using config file: ", viper.ConfigFileUsed())
	}

	log.Debug("Rancher Key: ", viper.GetString(rKey))
	rancherSecret := viper.GetString(rSecret)
	// mask the key for debug output
	l := len(rancherSecret) - 10
	if l > 0 {
		rp := strings.Repeat("*", l)
		replacer := strings.NewReplacer(rancherSecret[0:l], rp)
		log.Debug("Rancher Secret: ", replacer.Replace(rancherSecret))
	} else {
		log.Debug("Rancher Secret value is less than 10 chars cannot be valid value: ", rancherSecret)
	}
}
<commit_msg>Changing rancher env vals to use service account cattle env vals<commit_after>// Copyright © 2016 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"fmt"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const rKey string = "rancherKey"
const rSecret string = "rancherSecret"
const rURL string = "rancherURL"

var cfgFile string

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "ranchhand",
	Short: "Ranch hand helps out on the ranch",
	Long:  `Ranch hand is a set of cli tools for supporting a rancher cluster`,
	// Uncomment the following line if your bare application
	// has an action associated with it:
	//Run: func(cmd *cobra.Command, args []string) { }
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	// Here you will define your flags and configuration settings.
	// Cobra supports Persistent Flags, which, if defined here,
	// will be global for your application.
	RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ranchhand.yml)")
	RootCmd.PersistentFlags().String(rKey, "", "Rancher API key")
	RootCmd.PersistentFlags().String(rSecret, "", "Rancher API secret")
	RootCmd.PersistentFlags().String(rURL, "", "Rancher API url")
	viper.BindPFlags(RootCmd.PersistentFlags())
	//viper.BindPFlag("rancherKey", RootCmd.Flags().Lookup("rancherKey"))
	//viper.BindPFlag("rancherSecret", RootCmd.Flags().Lookup("rancherSecret"))
	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	// RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" { // enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}
	viper.SetConfigType("yaml")
	viper.SetConfigName("ranchhand")
	viper.AddConfigPath("/etc/ranchhand/")
	viper.AddConfigPath("$HOME/.ranchhand")
	viper.AddConfigPath("./")

	viper.BindEnv(rKey, "CATTLE_ACCESS_KEY") // need to bind name key to env KEY
	viper.BindEnv(rSecret, "CATTLE_SECRET_KEY")
	viper.BindEnv(rURL, "CATTLE_URL")
	viper.BindEnv("logginglevel", "LOGGING_LEVEL")

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err != nil {
		fmt.Println("Error reading config")
	}

	level, err := log.ParseLevel(strings.ToLower(viper.GetString("loggingLevel")))
	if err != nil {
		log.Error("Invalid logging level")
	} else {
		log.SetLevel(level)
		log.Debug("Logging level: ", level)
		log.Debug("Using config file: ", viper.ConfigFileUsed())
	}

	log.Debug("Rancher Key: ", viper.GetString(rKey))
	rancherSecret := viper.GetString(rSecret)
	// mask the key for debug output
	l := len(rancherSecret) - 10
	if l > 0 {
		rp := strings.Repeat("*", l)
		replacer := strings.NewReplacer(rancherSecret[0:l], rp)
		log.Debug("Rancher Secret: ", replacer.Replace(rancherSecret))
	} else {
		log.Debug("Rancher Secret value is less than 10 chars cannot be valid value: ", rancherSecret)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/oursky/ourd/oderr"
	"net/http"

	"github.com/oursky/ourd/authtoken"
	"github.com/oursky/ourd/oddb"
	"github.com/oursky/ourd/router"
)

type apiKeyValidatonPreprocessor struct {
	Key     string
	AppName string
}

func (p apiKeyValidatonPreprocessor) Preprocess(payload *router.Payload, response *router.Response) int {
	apiKey := payload.APIKey()
	if apiKey != p.Key {
		log.Debugf("Invalid APIKEY: %v", apiKey)
		response.Err = oderr.NewFmt(oderr.CannotVerifyAPIKey, "Cannot verify api key: %v", apiKey)
		return http.StatusUnauthorized
	}

	payload.AppName = p.AppName

	return http.StatusOK
}

type connPreprocessor struct {
	DBOpener func(string, string, string) (oddb.Conn, error)
	DBImpl   string
	Option   string
}

func (p connPreprocessor) Preprocess(payload *router.Payload, response *router.Response) int {
	log.Debugf("Opening DBConn: {%v %v %v}", p.DBImpl, payload.AppName, p.Option)

	conn, err := p.DBOpener(p.DBImpl, payload.AppName, p.Option)
	if err != nil {
		response.Err = err
		return http.StatusServiceUnavailable
	}
	payload.DBConn = conn

	log.Debugf("Get DB OK")

	return http.StatusOK
}

type tokenStorePreprocessor struct {
	authtoken.Store
}

func (p tokenStorePreprocessor) Preprocess(payload *router.Payload, response *router.Response) int {
	payload.TokenStore = p.Store
	return http.StatusOK
}

// UserAuthenticator provides preprocess method to authenicate a user
// with access token or non-login user without api key.
type userAuthenticator struct {
	// These two fields are for non-login user
	APIKey  string
	AppName string
}

func (author *userAuthenticator) Preprocess(payload *router.Payload, response *router.Response) int {
	tokenString := payload.AccessToken()
	if tokenString == "" {
		apiKey := payload.APIKey()
		if apiKey != author.APIKey {
			log.Debugf("Invalid APIKEY: %v", apiKey)
			response.Err = oderr.NewFmt(oderr.CannotVerifyAPIKey, "Cannot verify api key: %v", apiKey)
			return http.StatusUnauthorized
		}

		payload.AppName = author.AppName
	} else {
		store := payload.TokenStore
		token := authtoken.Token{}

		if err := store.Get(tokenString, &token); err != nil {
			if _, ok := err.(*authtoken.NotFoundError); ok {
				log.WithFields(log.Fields{
					"token": tokenString,
					"err":   err,
				}).Infoln("Token not found")

				response.Err = oderr.ErrAuthFailure
			} else {
				response.Err = err
			}
			return http.StatusUnauthorized
		}

		payload.AppName = token.AppName
		payload.UserInfoID = token.UserInfoID
	}

	return http.StatusOK
}

func injectUserIfPresent(payload *router.Payload, response *router.Response) int {
	if payload.UserInfoID == "" {
		log.Debugln("injectUser: empty UserInfoID, skipping")
		return http.StatusOK
	}

	conn := payload.DBConn
	userinfo := oddb.UserInfo{}
	if err := conn.GetUser(payload.UserInfoID, &userinfo); err != nil {
		log.Errorf("Cannot find UserInfo.ID = %#v\n", payload.UserInfoID)
		response.Err = err
		return http.StatusInternalServerError
	}

	payload.UserInfo = &userinfo

	return http.StatusOK
}

func injectDatabase(payload *router.Payload, response *router.Response) int {
	conn := payload.DBConn

	databaseID, _ := payload.Data["database_id"].(string)
	switch databaseID {
	case "_public":
		payload.Database = conn.PublicDB()
	case "_private":
		if payload.UserInfo != nil {
			payload.Database = conn.PrivateDB(payload.UserInfo.ID)
		} else {
			response.Err = errors.New("Authentication is needed for private DB access")
			return http.StatusUnauthorized
		}
	}

	return http.StatusOK
}
<commit_msg>Provide more appropriate error message when both api key and access token are empty, #92<commit_after>package main

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/oursky/ourd/oderr"
	"net/http"

	"github.com/oursky/ourd/authtoken"
	"github.com/oursky/ourd/oddb"
	"github.com/oursky/ourd/router"
)

type apiKeyValidatonPreprocessor struct {
	Key     string
	AppName string
}

func (p apiKeyValidatonPreprocessor) Preprocess(payload *router.Payload, response *router.Response) int {
	apiKey := payload.APIKey()
	if apiKey != p.Key {
		log.Debugf("Invalid APIKEY: %v", apiKey)
		response.Err = oderr.NewFmt(oderr.CannotVerifyAPIKey, "Cannot verify api key: %v", apiKey)
		return http.StatusUnauthorized
	}

	payload.AppName = p.AppName

	return http.StatusOK
}

type connPreprocessor struct {
	DBOpener func(string, string, string) (oddb.Conn, error)
	DBImpl   string
	Option   string
}

func (p connPreprocessor) Preprocess(payload *router.Payload, response *router.Response) int {
	log.Debugf("Opening DBConn: {%v %v %v}", p.DBImpl, payload.AppName, p.Option)

	conn, err := p.DBOpener(p.DBImpl, payload.AppName, p.Option)
	if err != nil {
		response.Err = err
		return http.StatusServiceUnavailable
	}
	payload.DBConn = conn

	log.Debugf("Get DB OK")

	return http.StatusOK
}

type tokenStorePreprocessor struct {
	authtoken.Store
}

func (p tokenStorePreprocessor) Preprocess(payload *router.Payload, response *router.Response) int {
	payload.TokenStore = p.Store
	return http.StatusOK
}

// UserAuthenticator provides preprocess method to authenicate a user
// with access token or non-login user without api key.
type userAuthenticator struct {
	// These two fields are for non-login user
	APIKey  string
	AppName string
}

func (author *userAuthenticator) Preprocess(payload *router.Payload, response *router.Response) int {
	tokenString := payload.AccessToken()
	if tokenString == "" {
		apiKey := payload.APIKey()
		if apiKey != author.APIKey {
			if author.APIKey != "" && apiKey == "" {
				// if a non-empty api key is set and we received empty
				// api key and access token, then client request
				// has no authentication information
				response.Err = oderr.NewFmt(oderr.AuthenticationInfoIncorrectErr, "Both api key and access token are empty")
			} else {
				response.Err = oderr.NewFmt(oderr.CannotVerifyAPIKey, "Cannot verify api key: `%v`", apiKey)
			}
			return http.StatusUnauthorized
		}

		payload.AppName = author.AppName
	} else {
		store := payload.TokenStore
		token := authtoken.Token{}

		if err := store.Get(tokenString, &token); err != nil {
			if _, ok := err.(*authtoken.NotFoundError); ok {
				log.WithFields(log.Fields{
					"token": tokenString,
					"err":   err,
				}).Infoln("Token not found")

				response.Err = oderr.ErrAuthFailure
			} else {
				response.Err = err
			}
			return http.StatusUnauthorized
		}

		payload.AppName = token.AppName
		payload.UserInfoID = token.UserInfoID
	}

	return http.StatusOK
}

func injectUserIfPresent(payload *router.Payload, response *router.Response) int {
	if payload.UserInfoID == "" {
		log.Debugln("injectUser: empty UserInfoID, skipping")
		return http.StatusOK
	}

	conn := payload.DBConn
	userinfo := oddb.UserInfo{}
	if err := conn.GetUser(payload.UserInfoID, &userinfo); err != nil {
		log.Errorf("Cannot find UserInfo.ID = %#v\n", payload.UserInfoID)
		response.Err = err
		return http.StatusInternalServerError
	}

	payload.UserInfo = &userinfo

	return http.StatusOK
}

func injectDatabase(payload *router.Payload, response *router.Response) int {
	conn := payload.DBConn

	databaseID, _ := payload.Data["database_id"].(string)
	switch databaseID {
	case "_public":
		payload.Database = conn.PublicDB()
	case "_private":
		if payload.UserInfo != nil {
			payload.Database = conn.PrivateDB(payload.UserInfo.ID)
		} else {
			response.Err = errors.New("Authentication is needed for private DB access")
			return http.StatusUnauthorized
		}
	}

	return http.StatusOK
}
<|endoftext|>


<commit_before>// Copyright © 2017 edwin <edwin.lzh@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/lvzhihao/uchatlib"
	"github.com/lvzhihao/zhiya/models"
	"github.com/lvzhihao/zhiya/uchat"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// syncCmd represents the sync command
var syncCmd = &cobra.Command{
	Use:   "sync",
	Short: "同步工具",
	Long:  `手工同步设备、群及用户相关信息`,
	Run: func(cmd *cobra.Command, args []string) {
		defer Logger.Sync()
		sugar := Logger.Sugar()
		action, err := cmd.Flags().GetString("action")
		if err != nil {
			sugar.Fatal(err)
		}
		db, err := gorm.Open("mysql", viper.GetString("mysql_dns"))
		if err != nil {
			sugar.Fatal(err)
		}
		defer db.Close()
		//db.LogMode(true)
		gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
			return viper.GetString("table_prefix") + "_" + defaultTableName
		}
		client := uchatlib.NewClient(viper.GetString("merchant_no"), viper.GetString("merchant_secret"))
		switch action {
		case "robot":
			if err := uchat.SyncRobots(client, db); err == nil {
				sugar.Info("robots sync success")
			} else {
				sugar.Warn(err)
			}
		case "chat":
			var err error
			var objs []models.Robot
			if robots, _ := cmd.Flags().GetString("robots"); robots != "" {
				err = db.Where("serial_no in (?)", strings.Split(robots, ",")).Find(&objs).Error
			} else {
				err = db.Find(&objs).Error
			}
			if err != nil {
				sugar.Fatal(err)
			}
			for _, robot := range objs {
				err := uchat.SyncRobotChatRooms(robot.SerialNo, client, db)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("chatromms sync success: %s\n", robot.SerialNo)
				}
			}
		case "member":
			var err error
			var objs []models.ChatRoom
			if chats, _ := cmd.Flags().GetString("chats"); chats != "" {
				err = db.Where("chat_room_serial_no in (?)", strings.Split(chats, ",")).Find(&objs).Error
			} else {
				err = db.Where("robot_status = ?", 10).Where("robot_in_status = ?", 0).Find(&objs).Error
			}
			if err != nil {
				sugar.Fatal(err)
			}
			for _, chat := range objs {
				err := uchat.SyncChatRoomMembers(chat.ChatRoomSerialNo, client)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("members sync success: %s\n", chat.ChatRoomSerialNo)
				}
			}
		case "chatstatus":
			rows, err := db.Model(&models.ChatRoom{}).Select("chat_room_serial_no").Rows()
			if err != nil {
				sugar.Fatal(err)
			}
			defer rows.Close()
			num := 0
			for rows.Next() {
				var no string
				err := rows.Scan(&no)
				if err != nil {
					sugar.Error(err)
				} else {
					err := uchat.SyncChatRoomStatus(no, client, db)
					if err != nil {
						sugar.Error(err)
					} else {
						sugar.Infof("success: %s", no)
					}
				}
				num++
			}
			sugar.Infof("count: %d", num)
		case "openmessage":
			chats, err := cmd.Flags().GetString("chats")
			if err != nil {
				sugar.Fatal(err)
			}
			for _, chat := range strings.Split(chats, ",") {
				err := uchatlib.SetChatRoomOpenGetMessage(chat, client)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("open GetMessage success: %s\n", chat)
				}
			}
		case "closemessage":
			chats, err := cmd.Flags().GetString("chats")
			if err != nil {
				sugar.Fatal(err)
			}
			for _, chat := range strings.Split(chats, ",") {
				err := uchatlib.SetChatRoomCloseGetMessage(chat, client)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("close GetMessage success: %s\n", chat)
				}
			}
		case "checkqrcode":
			var chatRooms []models.ChatRoom
			var err error
			if robots, _ := cmd.Flags().GetString("robots"); robots != "" {
				err = db.Where("robot_serial_no in (?)", strings.Split(robots, ",")).Where("robot_status = ?", 10).Where("robot_in_status = ?", 0).Where("(qr_code is null OR qr_code_expired_date < NOW())").Find(&chatRooms).Error
			} else {
				err = db.Where("robot_status = ?", 10).Where("robot_in_status = ?", 0).Where("(qr_code is null OR qr_code_expired_date < NOW())").Limit(10).Find(&chatRooms).Error
			}
			if err != nil {
				sugar.Fatal(err)
			}
			//sugar.Info(chatRooms)
			for _, room := range chatRooms {
				err := uchatlib.ApplyChatRoomQrCode(room.ChatRoomSerialNo, client)
				if err != nil {
					sugar.Warn(err)
				} else {
					sugar.Infof("create new qrcode success: %s\n", room.ChatRoomSerialNo)
				}
			}
		default:
			sugar.Warn("only support robot/chat/member/chatstatus")
		}
	},
}

func init() {
	RootCmd.AddCommand(syncCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	//syncCmd.PersistentFlags().String("action", "", "数据同步[robot/chat/member]")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// syncCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	syncCmd.Flags().String("action", "", "数据同步[robot/chat/user]")
	syncCmd.Flags().String("robots", "", "同步设备关联，多个设备请用半角逗号分隔")
	syncCmd.Flags().String("chats", "", "同步群关联，多个群请用半角逗号分隔")
}
<commit_msg>sync chatmembers<commit_after>// Copyright © 2017 edwin <edwin.lzh@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/lvzhihao/uchatlib"
	"github.com/lvzhihao/zhiya/models"
	"github.com/lvzhihao/zhiya/uchat"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// syncCmd represents the sync command
var syncCmd = &cobra.Command{
	Use:   "sync",
	Short: "同步工具",
	Long:  `手工同步设备、群及用户相关信息`,
	Run: func(cmd *cobra.Command, args []string) {
		defer Logger.Sync()
		sugar := Logger.Sugar()
		action, err := cmd.Flags().GetString("action")
		if err != nil {
			sugar.Fatal(err)
		}
		db, err := gorm.Open("mysql", viper.GetString("mysql_dns"))
		if err != nil {
			sugar.Fatal(err)
		}
		defer db.Close()
		//db.LogMode(true)
		gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
			return viper.GetString("table_prefix") + "_" + defaultTableName
		}
		client := uchatlib.NewClient(viper.GetString("merchant_no"), viper.GetString("merchant_secret"))
		switch action {
		case "robot":
			if err := uchat.SyncRobots(client, db); err == nil {
				sugar.Info("robots sync success")
			} else {
				sugar.Warn(err)
			}
		case "chat":
			var err error
			var objs []models.Robot
			if robots, _ := cmd.Flags().GetString("robots"); robots != "" {
				err = db.Where("serial_no in (?)", strings.Split(robots, ",")).Find(&objs).Error
			} else {
				err = db.Find(&objs).Error
			}
			if err != nil {
				sugar.Fatal(err)
			}
			for _, robot := range objs {
				err := uchat.SyncRobotChatRooms(robot.SerialNo, client, db)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("chatromms sync success: %s\n", robot.SerialNo)
				}
			}
		case "member":
			var err error
			var objs []models.ChatRoom
			if chats, _ := cmd.Flags().GetString("chats"); chats != "" {
				err = db.Where("chat_room_serial_no in (?)", strings.Split(chats, ",")).Find(&objs).Error
			} else {
				err = db.Where("robot_status = ?", 10).Where("robot_in_status = ?", 0).Find(&objs).Error
			}
			if err != nil {
				sugar.Fatal(err)
			}
			for _, chat := range objs {
				err := uchat.SyncChatRoomMembers(chat.ChatRoomSerialNo, client)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("members sync success: %s\n", chat.ChatRoomSerialNo)
				}
			}
		case "chatstatus":
			rows, err := db.Model(&models.ChatRoom{}).Select("chat_room_serial_no").Rows()
			if err != nil {
				sugar.Fatal(err)
			}
			defer rows.Close()
			num := 0
			for rows.Next() {
				var no string
				err := rows.Scan(&no)
				if err != nil {
					sugar.Error(err)
				} else {
					err := uchat.SyncChatRoomStatus(no, client, db)
					if err != nil {
						sugar.Error(err)
					} else {
						sugar.Infof("success: %s", no)
					}
				}
				num++
			}
			sugar.Infof("count: %d", num)
		case "chatmembers":
			// 有效群的会员数据更新
			rows, err := db.Model(&models.ChatRoom{}).Select("chat_room_serial_no").Where("status = ?", 10).Rows()
			if err != nil {
				sugar.Fatal(err)
			}
			defer rows.Close()
			num := 0
			for rows.Next() {
				var no string
				err := rows.Scan(&no)
				if err != nil {
					sugar.Error(err)
				} else {
					err := uchat.SyncChatRoomMembers(no, client)
					if err != nil {
						sugar.Error(err)
					} else {
						sugar.Infof("members sync success: %s\n", no)
					}
				}
				num++
			}
			sugar.Infof("count: %d", num)
		case "openmessage":
			chats, err := cmd.Flags().GetString("chats")
			if err != nil {
				sugar.Fatal(err)
			}
			for _, chat := range strings.Split(chats, ",") {
				err := uchatlib.SetChatRoomOpenGetMessage(chat, client)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("open GetMessage success: %s\n", chat)
				}
			}
		case "closemessage":
			chats, err := cmd.Flags().GetString("chats")
			if err != nil {
				sugar.Fatal(err)
			}
			for _, chat := range strings.Split(chats, ",") {
				err := uchatlib.SetChatRoomCloseGetMessage(chat, client)
				if err != nil {
					sugar.Fatal(err)
				} else {
					sugar.Infof("close GetMessage success: %s\n", chat)
				}
			}
		case "checkqrcode":
			var chatRooms []models.ChatRoom
			var err error
			if robots, _ := cmd.Flags().GetString("robots"); robots != "" {
				err = db.Where("robot_serial_no in (?)", strings.Split(robots, ",")).Where("robot_status = ?", 10).Where("robot_in_status = ?", 0).Where("(qr_code is null OR qr_code_expired_date < NOW())").Find(&chatRooms).Error
			} else {
				err = db.Where("robot_status = ?", 10).Where("robot_in_status = ?", 0).Where("(qr_code is null OR qr_code_expired_date < NOW())").Limit(10).Find(&chatRooms).Error
			}
			if err != nil {
				sugar.Fatal(err)
			}
			//sugar.Info(chatRooms)
			for _, room := range chatRooms {
				err := uchatlib.ApplyChatRoomQrCode(room.ChatRoomSerialNo, client)
				if err != nil {
					sugar.Warn(err)
				} else {
					sugar.Infof("create new qrcode success: %s\n", room.ChatRoomSerialNo)
				}
			}
		default:
			sugar.Warn("only support robot/chat/member/chatstatus/chatmembers")
		}
	},
}

func init() {
	RootCmd.AddCommand(syncCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	//syncCmd.PersistentFlags().String("action", "", "数据同步[robot/chat/member]")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// syncCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	syncCmd.Flags().String("action", "", "数据同步[robot/chat/user]")
	syncCmd.Flags().String("robots", "", "同步设备关联，多个设备请用半角逗号分隔")
	syncCmd.Flags().String("chats", "", "同步群关联，多个群请用半角逗号分隔")
}
<|endoftext|>


<commit_before>package vfmd

import (
	"bytes"
	"unicode/utf8"
)
import "io"

type Preprocessor struct {
	Chunks  []Chunk
	Pending []byte
	state   int
}

// Preprocessor states
const (
	preproMaybeBOM = iota
	preproNormal
	preproCR
)

type Chunk struct {
	Bytes []byte
	Type  ChunkType
}

type ChunkType int

const (
	ChunkIgnoredBOM ChunkType = iota
	ChunkUnchangedRunes
)

// statically ensure that certain interfaces are implemented by Preprocessor
var _ io.Writer = &Preprocessor{}

func (p *Preprocessor) Write(buf []byte) (int, error) {
	for _, b := range buf {
		p.WriteByte(b)
	}
	return len(buf), nil
}

func (p *Preprocessor) WriteByte(b byte) error {
	const (
		CR = '\r'
		LF = '\n'
	)

	// ignore Byte-Order-Mark [#document]
	// TODO(akavel): add more tests
	if p.state == preproMaybeBOM {
		BOM := []byte{0xEF, 0xBB, 0xBF}
		p.Pending = append(p.Pending, b)
		switch {
		case !bytes.HasPrefix(BOM, p.Pending):
			p.state = preproNormal
			buf := p.Pending
			p.Pending = nil
			p.Write(buf)
			return nil
		case len(p.Pending) == len(BOM):
			p.Chunks = append(p.Chunks, Chunk{
				Bytes: p.Pending,
				Type:  ChunkIgnoredBOM,
			})
			p.Pending = nil
			p.state = preproNormal
			return nil
		default: // still not sure
			return nil
		}
	}

	// Flush old state if pending
	if b != LF && p.state == preproCR {
		p.state = preproNormal
		p.normalChunk(CR)
	}

	// Detect invalid UTF-8 and assume it's ISO-8859-1 then. [#document]
	// TODO(akavel): add tests
	if b > utf8.RuneSelf {
		// May be an UTF-8 encoded rune; or assume ISO-8859-1 if invalid.
		p.Pending = append(p.Pending, b)
		if !utf8.FullRune(p.Pending) {
			// Still cannot say if valid or invalid rune
			return nil
		}
		buf := p.Pending
		p.Pending = nil
		r, _ := utf8.DecodeRune(buf)
		if r == utf8.RuneError {
			// TODO(akavel): add chunk marking start of invalid utf8
			p.Write(iso2utf(buf...))
			// TODO(akavel): add chunk marking end of invalid utf8
		} else {
			p.normalChunk(buf...)
		}
		return nil
	}
	// If any bytes are pending, they failed to build a complete UTF-8 rune; flush them
	if len(p.Pending) > 0 {
		buf := p.Pending
		p.Pending = nil
		// TODO(akavel): add chunk marking start of invalid utf8
		p.Write(iso2utf(buf...))
		// TODO(akavel): add chunk marking end of invalid utf8
	}

	// TODO(akavel): convert CRLF to LF [#characters]
	switch {
	case b == LF && p.state == preproCR:
		// CRLF detected
		p.state = preproNormal
		p.Chunks = append(p.Chunks, Chunk{
			Bytes: []byte{LF},
			Type:  ChunkNormalizedCRLF,
		})
		return nil
	case b == CR:
		p.state = preproCR
		return nil
	}

	// TODO(akavel): expand tabs to spaces [#lines]

	// TODO(akavel): WIP
	p.normalChunk(b)
	return nil
}

func (p *Preprocessor) normalChunk(b ...byte) {
	n := len(p.Chunks)
	if n == 0 || p.Chunks[n-1].Type != ChunkUnchangedRunes {
		p.Chunks = append(p.Chunks, Chunk{Type: ChunkUnchangedRunes})
		n++
	}
	p.Chunks[n-1].Bytes = append(p.Chunks[n-1].Bytes, b...)
}

func iso2utf(buf ...byte) []byte {
	out := make([]byte, 0, 2*len(buf))
	for _, b := range buf {
		r := rune(b)
		n := utf8.RuneLen(r) // 1 or 2
		pos := len(out)
		out = out[:pos+n]
		utf8.EncodeRune(out[pos:], r)
	}
	return out
}
<commit_msg>attempt at adding CRLF->LF correction<commit_after>package vfmd

import (
	"bytes"
	"unicode/utf8"
)
import "io"

type Preprocessor struct {
	Chunks  []Chunk
	Pending []byte
	state   int
}

// Preprocessor states
const (
	preproMaybeBOM = iota
	preproNormal
	preproCR
)

type Chunk struct {
	Bytes []byte
	Type  ChunkType
}

type ChunkType int

const (
	ChunkIgnoredBOM ChunkType = iota
	ChunkUnchangedRunes
	ChunkNormalizedCRLF
)

// statically ensure that certain interfaces are implemented by Preprocessor
var _ io.Writer = &Preprocessor{}

func (p *Preprocessor) Write(buf []byte) (int, error) {
	for _, b := range buf {
		p.WriteByte(b)
	}
	return len(buf), nil
}

func (p *Preprocessor) WriteByte(b byte) error {
	const (
		CR = '\r'
		LF = '\n'
	)

	// ignore Byte-Order-Mark [#document]
	// TODO(akavel): add more tests
	if p.state == preproMaybeBOM {
		BOM := []byte{0xEF, 0xBB, 0xBF}
		p.Pending = append(p.Pending, b)
		switch {
		case !bytes.HasPrefix(BOM, p.Pending):
			p.state = preproNormal
			buf := p.Pending
			p.Pending = nil
			p.Write(buf)
			return nil
		case len(p.Pending) == len(BOM):
			p.Chunks = append(p.Chunks, Chunk{
				Bytes: p.Pending,
				Type:  ChunkIgnoredBOM,
			})
			p.Pending = nil
			p.state = preproNormal
			return nil
		default: // still not sure
			return nil
		}
	}

	if p.state == preproCR {
		if b == LF {
			// CRLF detected
			p.state = preproNormal
			p.Chunks = append(p.Chunks, Chunk{
				Bytes: []byte{LF},
				Type:  ChunkNormalizedCRLF,
			})
			return nil
		}
		// Flush the pending CR
		p.state = preproNormal
		p.normalChunk(CR)
	}

	// Detect invalid UTF-8 and assume it's ISO-8859-1 then. [#document]
	// TODO(akavel): add tests
	if b > utf8.RuneSelf {
		// May be an UTF-8 encoded rune; or assume ISO-8859-1 if invalid.
		p.Pending = append(p.Pending, b)
		if !utf8.FullRune(p.Pending) {
			// Still cannot say if valid or invalid rune
			return nil
		}
		buf := p.Pending
		p.Pending = nil
		r, _ := utf8.DecodeRune(buf)
		if r == utf8.RuneError {
			// TODO(akavel): add chunk marking start of invalid utf8
			p.Write(iso2utf(buf...))
			// TODO(akavel): add chunk marking end of invalid utf8
		} else {
			p.normalChunk(buf...)
		}
		return nil
	}
	// If any bytes are pending, they failed to build a complete UTF-8 rune; flush them
	if len(p.Pending) > 0 {
		buf := p.Pending
		p.Pending = nil
		// TODO(akavel): add chunk marking start of invalid utf8
		p.Write(iso2utf(buf...))
		// TODO(akavel): add chunk marking end of invalid utf8
	}

	// Convert CRLF to LF. [#characters]
	if b == CR {
		p.state = preproCR
		return nil
	}

	// TODO(akavel): expand tabs to spaces [#lines]

	// TODO(akavel): WIP
	p.normalChunk(b)
	return nil
}

func (p *Preprocessor) normalChunk(b ...byte) {
	n := len(p.Chunks)
	if n == 0 || p.Chunks[n-1].Type != ChunkUnchangedRunes {
		p.Chunks = append(p.Chunks, Chunk{Type: ChunkUnchangedRunes})
		n++
	}
	p.Chunks[n-1].Bytes = append(p.Chunks[n-1].Bytes, b...)
}

func iso2utf(buf ...byte) []byte {
	out := make([]byte, 0, 2*len(buf))
	for _, b := range buf {
		r := rune(b)
		n := utf8.RuneLen(r) // 1 or 2
		pos := len(out)
		out = out[:pos+n]
		utf8.EncodeRune(out[pos:], r)
	}
	return out
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

// greenhouse implements a bazel remote cache service [1]
// supporting arbitrarily many workspaces stored within the same
// top level directory.
//
// the first path segment in each {PUT,GET} request is mapped to an individual
// workspace cache, the remaining segments should follow [2].
//
// nursery assumes you are using SHA256
//
// [1] https://docs.bazel.build/versions/master/remote-caching.html
// [2] https://docs.bazel.build/versions/master/remote-caching.html#http-caching-protocol
package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"k8s.io/test-infra/greenhouse/diskcache"
	"k8s.io/test-infra/greenhouse/diskutil"
	"k8s.io/test-infra/prow/logrusutil"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
)

var dir = flag.String("dir", "", "location to store cache entries on disk")
var host = flag.String("host", "", "host address to listen on")
var cachePort = flag.Int("cache-port", 8080, "port to listen on for cache requests")
var metricsPort = flag.Int("metrics-port", 9090, "port to listen on for prometheus metrics scraping")
var metricsUpdateInterval = flag.Duration("metrics-update-interval", time.Minute,
	"interval between updating disk metrics")

// eviction knobs
var minPercentBlocksFree = flag.Float64("min-percent-blocks-free", 10,
	"minimum percent of blocks free on --dir's disk before evicting entries")
var evictUntilPercentBlocksFree = flag.Float64("evict-until-percent-blocks-free", 20,
	"continue evicting from the cache until at least this percent of blocks are free")
var diskCheckInterval = flag.Duration("disk-check-interval", time.Second*30,
	"interval between checking disk usage (and potentially evicting entries)")

// NOTE: remount is a bit of a hack, unfortunately the kubernetes volumes
// don't really support this and to cleanly track entry access times we
// want to use a volume with strictatime,lazytime (and not noatime or relatime)
// so that file access times *are* recorded but are lazily flushed to the disk
// https://lwn.net/Articles/621046/
// https://unix.stackexchange.com/questions/276858/why-is-ext4-filesystem-mounted-with-both-relatime-and-lazytime
var remount = flag.Bool("remount", false,
	"attempt to remount --dir with strictatime,lazyatime to improve eviction")

// prometheusMetrics are served by /prometheus on the metrics port
type prometheusMetrics struct {
	DiskStats  map[string]prometheus.Gauge
	CacheStats map[string]prometheus.Counter
}

var (
	promMetrics = prometheusMetrics{
		DiskStats: map[string]prometheus.Gauge{
			"free": prometheus.NewGauge(prometheus.GaugeOpts{
				Name: "bazel_cache_disk_free",
				Help: "Free bytes on bazel cache disk",
			}),
			"used": prometheus.NewGauge(prometheus.GaugeOpts{
				Name: "bazel_cache_disk_used",
				Help: "Used bytes on bazel cache disk",
			}),
			"total": prometheus.NewGauge(prometheus.GaugeOpts{
				Name: "bazel_cache_disk_total",
				Help: "Total bytes on bazel cache disk",
			}),
		},
		CacheStats: map[string]prometheus.Counter{
			"hits": prometheus.NewCounter(prometheus.CounterOpts{
				Name: "bazel_cache_hits",
				Help: "Approximate number of cache hits since last server start",
			}),
			"misses": prometheus.NewCounter(prometheus.CounterOpts{
				Name: "bazel_cache_misses",
				Help: "Approximate number of cache misses since last server start",
			}),
		},
	}
)

func init() {
	// register logger
	logrus.SetFormatter(
		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "greenhouse"}),
	)
	logrus.SetOutput(os.Stdout)
	// register metrics
	for _, stat := range promMetrics.DiskStats {
		prometheus.MustRegister(stat)
	}
	for _, stat := range promMetrics.CacheStats {
		prometheus.MustRegister(stat)
	}
}

func main() {
	flag.Parse()
	if *dir == "" {
		logrus.Fatal("--dir must be set!")
	}
	if *remount {
		device, mount, err := diskutil.FindMountForPath(*dir)
		if err != nil {
			logrus.WithError(err).Errorf("Failed to find mountpoint for %s", *dir)
		} else {
			logrus.Warnf(
				"Attempting to remount %s on %s with 'strictatime,lazyatime'",
				device, mount,
			)
			err = diskutil.Remount(device, mount, "strictatime,lazytime")
			if err != nil {
				logrus.WithError(err).Error("Failed to remount with lazyatime!")
			} else {
				logrus.Info("Remount complete")
			}
		}
	}

	cache := diskcache.NewCache(*dir)
	go cache.MonitorDiskAndEvict(
		*diskCheckInterval,
		*minPercentBlocksFree, *evictUntilPercentBlocksFree,
	)

	go updateMetrics(*metricsUpdateInterval, cache.DiskRoot())

	// listen for prometheus scraping
	metricsMux := http.NewServeMux()
	metricsMux.Handle("/prometheus", promhttp.Handler())
	metricsAddr := fmt.Sprintf("%s:%d", *host, *metricsPort)
	go func() {
		logrus.Infof("Metrics Listening on: %s", metricsAddr)
		logrus.WithField("mux", "metrics").WithError(
			http.ListenAndServe(metricsAddr, metricsMux),
		).Fatal("ListenAndServe returned.")
	}()

	// listen for cache requests
	cacheMux := http.NewServeMux()
	cacheMux.Handle("/", cacheHandler(cache))
	cacheAddr := fmt.Sprintf("%s:%d", *host, *cachePort)
	logrus.Infof("Cache Listening on: %s", cacheAddr)
	logrus.WithField("mux", "cache").WithError(
		http.ListenAndServe(cacheAddr, cacheMux),
	).Fatal("ListenAndServe returned.")
}

// file not found error, used below
var errNotFound = errors.New("entry not found")

func cacheHandler(cache *diskcache.Cache) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger := logrus.WithFields(logrus.Fields{
			"method": r.Method,
			"path":   r.URL.Path,
		})
		// parse and validate path
		// the last segment should be a hash, and the
		// the second to last segment should be "ac" or "cas"
		parts := strings.Split(r.URL.Path, "/")
		if len(parts) < 3 {
			logger.Warn("received an invalid request")
			http.Error(w, "invalid location", http.StatusBadRequest)
			return
		}
		hash := parts[len(parts)-1]
		acOrCAS := parts[len(parts)-2]
		if acOrCAS != "ac" && acOrCAS != "cas" {
			logger.Warn("received an invalid request at path")
			http.Error(w, "invalid location", http.StatusBadRequest)
			return
		}

		// actually handle request depending on method
		switch m := r.Method; m {
		// handle retreival
		case http.MethodGet:
			err := cache.Get(r.URL.Path, func(exists bool, contents io.ReadSeeker) error {
				if !exists {
					promMetrics.CacheStats["misses"].Inc()
					return errNotFound
				}
				promMetrics.CacheStats["hits"].Inc()
				http.ServeContent(w, r, "", time.Time{}, contents)
				return nil
			})
			if err != nil {
				// file not present
				if err == errNotFound {
					http.Error(w, err.Error(), http.StatusNotFound)
					return
				}
				// unknown error
				logger.WithError(err).Error("error getting key")
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

		// handle upload
		case http.MethodPut:
			// only hash CAS, not action cache
			// the action cache is hash -> metadata
			// the CAS is well, a CAS, which we can hash...
			if acOrCAS != "cas" {
				hash = ""
			}
			err := cache.Put(r.URL.Path, r.Body, hash)
			if err != nil {
				logger.WithError(err).Errorf("Failed to put: %v", r.URL.Path)
				http.Error(w, "failed to put in cache", http.StatusInternalServerError)
				return
			}

		// handle unsupported methods...
		default:
			logger.Warn("received an invalid request method")
			http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
		}
	})
}

// helper to update disk metrics
func updateMetrics(interval time.Duration, diskRoot string) {
	logger := logrus.WithField("sync-loop", "updateMetrics")
	ticker := time.NewTicker(interval)
	for ; true; <-ticker.C {
		logger.Info("tick")
		_, bytesFree, bytesUsed, err := diskutil.GetDiskUsage(diskRoot)
		if err != nil {
			logger.WithError(err).Error("Failed to get disk metrics")
		} else {
			promMetrics.DiskStats["free"].Set(float64(bytesFree))
			promMetrics.DiskStats["used"].Set(float64(bytesUsed))
			promMetrics.DiskStats["total"].Set(float64(bytesFree + bytesUsed))
		}
	}
}
<commit_msg>use gb for cache metrics<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

// greenhouse implements a bazel remote cache service [1]
// supporting arbitrarily many workspaces stored within the same
// top level directory.
//
// the first path segment in each {PUT,GET} request is mapped to an individual
// workspace cache, the remaining segments should follow [2].
//
// nursery assumes you are using SHA256
//
// [1] https://docs.bazel.build/versions/master/remote-caching.html
// [2] https://docs.bazel.build/versions/master/remote-caching.html#http-caching-protocol
package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"k8s.io/test-infra/greenhouse/diskcache"
	"k8s.io/test-infra/greenhouse/diskutil"
	"k8s.io/test-infra/prow/logrusutil"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
)

var dir = flag.String("dir", "", "location to store cache entries on disk")
var host = flag.String("host", "", "host address to listen on")
var cachePort = flag.Int("cache-port", 8080, "port to listen on for cache requests")
var metricsPort = flag.Int("metrics-port", 9090, "port to listen on for prometheus metrics scraping")
var metricsUpdateInterval = flag.Duration("metrics-update-interval", time.Minute,
	"interval between updating disk metrics")

// eviction knobs
var minPercentBlocksFree = flag.Float64("min-percent-blocks-free", 10,
	"minimum percent of blocks free on --dir's disk before evicting entries")
var evictUntilPercentBlocksFree = flag.Float64("evict-until-percent-blocks-free", 20,
	"continue evicting from the cache until at least this percent of blocks are free")
var diskCheckInterval = flag.Duration("disk-check-interval", time.Second*30,
	"interval between checking disk usage (and potentially evicting entries)")

// NOTE: remount is a bit of a hack, unfortunately the kubernetes volumes
// don't really support this and to cleanly track entry access times we
// want to use a volume with strictatime,lazytime (and not noatime or relatime)
// so that file access times *are* recorded but are lazily flushed to the disk
// https://lwn.net/Articles/621046/
// https://unix.stackexchange.com/questions/276858/why-is-ext4-filesystem-mounted-with-both-relatime-and-lazytime
var remount = flag.Bool("remount", false,
	"attempt to remount --dir with strictatime,lazyatime to improve eviction")

// prometheusMetrics are served by /prometheus on the metrics port
type prometheusMetrics struct {
	DiskStats  map[string]prometheus.Gauge
	CacheStats map[string]prometheus.Counter
}

var (
	promMetrics = prometheusMetrics{
		DiskStats: map[string]prometheus.Gauge{
			"free": prometheus.NewGauge(prometheus.GaugeOpts{
				Name: "bazel_cache_disk_free",
				Help: "Free gb on bazel cache disk",
			}),
			"used": prometheus.NewGauge(prometheus.GaugeOpts{
				Name: "bazel_cache_disk_used",
				Help: "Used gb on bazel cache disk",
			}),
			"total": prometheus.NewGauge(prometheus.GaugeOpts{
				Name: "bazel_cache_disk_total",
				Help: "Total gb on bazel cache disk",
			}),
		},
		CacheStats: map[string]prometheus.Counter{
			"hits": prometheus.NewCounter(prometheus.CounterOpts{
				Name: "bazel_cache_hits",
				Help: "Approximate number of cache hits since last server start",
			}),
			"misses": prometheus.NewCounter(prometheus.CounterOpts{
				Name: "bazel_cache_misses",
				Help: "Approximate number of cache misses since last server start",
			}),
		},
	}
)

func init() {
	// register logger
	logrus.SetFormatter(
		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "greenhouse"}),
	)
	logrus.SetOutput(os.Stdout)
	// register metrics
	for _, stat := range promMetrics.DiskStats {
		prometheus.MustRegister(stat)
	}
	for _, stat := range promMetrics.CacheStats {
		prometheus.MustRegister(stat)
	}
}

func main() {
	flag.Parse()
	if *dir == "" {
		logrus.Fatal("--dir must be set!")
	}
	if *remount {
		device, mount, err := diskutil.FindMountForPath(*dir)
		if err != nil {
			logrus.WithError(err).Errorf("Failed to find mountpoint for %s", *dir)
		} else {
			logrus.Warnf(
				"Attempting to remount %s on %s with 'strictatime,lazyatime'",
				device, mount,
			)
			err = diskutil.Remount(device, mount, "strictatime,lazytime")
			if err != nil {
				logrus.WithError(err).Error("Failed to remount with lazyatime!")
			} else {
				logrus.Info("Remount complete")
			}
		}
	}

	cache := diskcache.NewCache(*dir)
	go cache.MonitorDiskAndEvict(
		*diskCheckInterval,
		*minPercentBlocksFree, *evictUntilPercentBlocksFree,
	)

	go updateMetrics(*metricsUpdateInterval, cache.DiskRoot())

	// listen for prometheus scraping
	metricsMux := http.NewServeMux()
	metricsMux.Handle("/prometheus", promhttp.Handler())
	metricsAddr := fmt.Sprintf("%s:%d", *host, *metricsPort)
	go func() {
		logrus.Infof("Metrics Listening on: %s", metricsAddr)
		logrus.WithField("mux", "metrics").WithError(
			http.ListenAndServe(metricsAddr, metricsMux),
		).Fatal("ListenAndServe returned.")
	}()

	// listen for cache requests
	cacheMux := http.NewServeMux()
	cacheMux.Handle("/", cacheHandler(cache))
	cacheAddr := fmt.Sprintf("%s:%d", *host, *cachePort)
	logrus.Infof("Cache Listening on: %s", cacheAddr)
	logrus.WithField("mux", "cache").WithError(
		http.ListenAndServe(cacheAddr, cacheMux),
	).Fatal("ListenAndServe returned.")
}

// file not found error, used below
var errNotFound = errors.New("entry not found")

func cacheHandler(cache *diskcache.Cache) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger := logrus.WithFields(logrus.Fields{
			"method": r.Method,
			"path":   r.URL.Path,
		})
		// parse and validate path
		// the last segment should be a hash, and the
		// the second to last segment should be "ac" or "cas"
		parts := strings.Split(r.URL.Path, "/")
		if len(parts) < 3 {
			logger.Warn("received an invalid request")
			http.Error(w, "invalid location", http.StatusBadRequest)
			return
		}
		hash := parts[len(parts)-1]
		acOrCAS := parts[len(parts)-2]
		if acOrCAS != "ac" && acOrCAS != "cas" {
			logger.Warn("received an invalid request at path")
			http.Error(w, "invalid location", http.StatusBadRequest)
			return
		}

		// actually handle request depending on method
		switch m := r.Method; m {
		// handle retreival
		case http.MethodGet:
			err := cache.Get(r.URL.Path, func(exists bool, contents io.ReadSeeker) error {
				if !exists {
					promMetrics.CacheStats["misses"].Inc()
					return errNotFound
				}
				promMetrics.CacheStats["hits"].Inc()
				http.ServeContent(w, r, "", time.Time{}, contents)
				return nil
			})
			if err != nil {
				// file not present
				if err == errNotFound {
					http.Error(w, err.Error(), http.StatusNotFound)
					return
				}
				// unknown error
				logger.WithError(err).Error("error getting key")
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

		// handle upload
		case http.MethodPut:
			// only hash CAS, not action cache
			// the action cache is hash -> metadata
			// the CAS is well, a CAS, which we can hash...
			if acOrCAS != "cas" {
				hash = ""
			}
			err := cache.Put(r.URL.Path, r.Body, hash)
			if err != nil {
				logger.WithError(err).Errorf("Failed to put: %v", r.URL.Path)
				http.Error(w, "failed to put in cache", http.StatusInternalServerError)
				return
			}

		// handle unsupported methods...
		default:
			logger.Warn("received an invalid request method")
			http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
		}
	})
}

// helper to update disk metrics
func updateMetrics(interval time.Duration, diskRoot string) {
	logger := logrus.WithField("sync-loop", "updateMetrics")
	ticker := time.NewTicker(interval)
	for ; true; <-ticker.C {
		logger.Info("tick")
		_, bytesFree, bytesUsed, err := diskutil.GetDiskUsage(diskRoot)
		if err != nil {
			logger.WithError(err).Error("Failed to get disk metrics")
		} else {
			promMetrics.DiskStats["free"].Set(float64(bytesFree) / 1e9)
			promMetrics.DiskStats["used"].Set(float64(bytesUsed) / 1e9)
			promMetrics.DiskStats["total"].Set(float64(bytesFree+bytesUsed) / 1e9)
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"github.com/spf13/viper"
	"log"
)

var config *viper.Viper

// Init is an exported method that takes the environment starts the viper
// (external lib) and returns the configuration struct.
func Init(env string) {
	v := viper.New()
	v.SetConfigType("yaml")
	v.SetConfigName(env)
	v.AddConfigPath("../../")
	fmt.Println(v)
	if err := v.ReadInConfig(); err != nil {
		log.Fatal("error on parsing configuration file")
	}
	config = v
}

func GetConfig() *viper.Viper {
	return config
}
<commit_msg>fix config file location<commit_after>package config

import (
	"fmt"
	"github.com/spf13/viper"
	"log"
)

var config *viper.Viper

// Init is an exported method that takes the environment starts the viper
// (external lib) and returns the configuration struct.
func Init(env string) {
	v := viper.New()
	v.SetConfigType("yaml")
	v.SetConfigName(env)
	v.AddConfigPath(".")
	v.AddConfigPath("config")
	fmt.Println(v)
	if err := v.ReadInConfig(); err != nil {
		log.Fatal("error on parsing configuration file", err)
	}
	config = v
}

func GetConfig() *viper.Viper {
	return config
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"os"
	"os/user"
	"path"

	"github.com/gsamokovarov/jump/scoring"
)

// The default configuration.
var Default Config

// A config represents the config directory and the file that stores the score
// values.
type Config struct {
	Dir    string
	Scores *os.File
}

// Write the input scoring entries to a file.
//
// Formats them in a similar format of autojump.
func (c *Config) WriteEntries(entries *scoring.Entries) error {
	buffer := []byte{}

	for _, entry := range *entries {
		buffer = append(buffer, formatEntry(entry)...)
	}

	_, err := c.Scores.Write(buffer)

	return err
}

func formatEntry(entry scoring.Entry) []byte {
	return []byte(fmt.Sprintf("%f %s\n", entry.CalculateScore(), entry.Path))
}

const (
	defaultScoreFile = "scores.txt"
	defaultDirName   = ".jump"
)

// Setups the config folder from a directory path.
//
// If the directories don't already exists, they are created and if the score
// file is present, it is loaded.
func Setup(dir string) (*Config, error) {
	// We get the directory check for free form os.MkdirAll.
	if err := os.MkdirAll(dir, os.ModeDir); err != nil {
		return nil, err
	}

	dirPath := path.Join(dir, defaultScoreFile)
	scores, err := os.OpenFile(dirPath, os.O_APPEND, os.FileMode(0644))
	if err == nil {
		return nil, err
	}

	return &Config{dir, scores}, nil
}

// Setups the config folder from a directory path.
//
// If the directory path is an empty string, the path is automatically guessed.
func SetupDefault(dir string) (*Config, error) {
	dir, err := normalizeDir(dir)
	if err != nil {
		return nil, err
	}

	return Setup(dir)
}

func normalizeDir(dir string) (string, error) {
	if len(dir) == 0 {
		usr, err := user.Current()
		if err != nil {
			return dir, err
		}

		homeDir := usr.HomeDir
		return path.Join(homeDir, defaultDirName), nil
	}

	return dir, nil
}
<commit_msg>Drop the default config reference<commit_after>package config

import (
	"fmt"
	"os"
	"os/user"
	"path"

	"github.com/gsamokovarov/jump/scoring"
)

// A config represents the config directory and the file that stores the score
// values.
type Config struct {
	Dir    string
	Scores *os.File
}

// Write the input scoring entries to a file.
//
// Formats them in a similar format of autojump.
func (c *Config) WriteEntries(entries *scoring.Entries) error {
	buffer := []byte{}

	for _, entry := range *entries {
		buffer = append(buffer, formatEntry(entry)...)
	}

	_, err := c.Scores.Write(buffer)

	return err
}

func formatEntry(entry scoring.Entry) []byte {
	return []byte(fmt.Sprintf("%f %s\n", entry.CalculateScore(), entry.Path))
}

const (
	defaultScoreFile = "scores.txt"
	defaultDirName   = ".jump"
)

// Setups the config folder from a directory path.
//
// If the directories don't already exists, they are created and if the score
// file is present, it is loaded.
func Setup(dir string) (*Config, error) {
	// We get the directory check for free form os.MkdirAll.
	if err := os.MkdirAll(dir, os.ModeDir); err != nil {
		return nil, err
	}

	dirPath := path.Join(dir, defaultScoreFile)
	scores, err := os.OpenFile(dirPath, os.O_APPEND, os.FileMode(0644))
	if err == nil {
		return nil, err
	}

	return &Config{dir, scores}, nil
}

// Setups the config folder from a directory path.
//
// If the directory path is an empty string, the path is automatically guessed.
func SetupDefault(dir string) (*Config, error) {
	dir, err := normalizeDir(dir)
	if err != nil {
		return nil, err
	}

	return Setup(dir)
}

func normalizeDir(dir string) (string, error) {
	if len(dir) == 0 {
		usr, err := user.Current()
		if err != nil {
			return dir, err
		}

		homeDir := usr.HomeDir
		return path.Join(homeDir, defaultDirName), nil
	}

	return dir, nil
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/ungerik/go-start/debug"
)

var Logger = log.New(os.Stderr, "", log.LstdFlags)

type Package interface {
	Name() string
	Init() error
	io.Closer
}

var Packages []Package

func Load(configFile string, packages ...Package) {
	debug.Nop()
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		Logger.Panicf("Error while reading config file %s: %s", configFile, err)
	}
	Packages = packages
	switch path.Ext(configFile) {
	case ".json":
		// Compact JSON to make it easier to extract JSON per package
		var buf bytes.Buffer
		err = json.Compact(&buf, data)
		if err != nil {
			Logger.Panicf("Error in JSON config file %s: %s", configFile, err)
		}
		data = buf.Bytes()

		// Unmarshal packages in given order
		for _, pkg := range packages {
			// Extract JSON only for this package
			key := []byte(`"` + pkg.Name() + `":{`)
			begin := bytes.Index(data, key)
			if begin == -1 {
				continue
			}
			begin += len(key) - 1
			end := 0
			braceCounter := 0
			for i := begin; i < len(data); i++ {
				switch data[i] {
				case '{':
					braceCounter++
				case '}':
					braceCounter--
				}
				if braceCounter == 0 {
					end = i + 1
					break
				}
			}

			err = json.Unmarshal(data[begin:end], pkg)
			if err != nil {
				Logger.Panicf("Error while unmarshalling JSON from config file %s: %s", configFile, err)
			}
			err := pkg.Init()
			if err != nil {
				Logger.Panicf("Error while initializing package %s: %s", pkg.Name(), err)
			}
		}

	default:
		panic("Unsupported config file: " + configFile)
	}

	for _, pkg := range packages {
		err := pkg.Init()
		if err != nil {
			log.Panicf("Error while initializing package %s: %s", pkg.Name(), err)
		}
	}
}

func Close() {
	for i := len(Packages) - 1; i >= 0; i-- {
		err := Packages[i].Close()
		if err != nil {
			Logger.Println("Error while closing package %s: %s", Packages[i].Name(), err)
		}
	}
}
<commit_msg>fixed double config Init()<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/ungerik/go-start/debug"
)

var Logger = log.New(os.Stderr, "", log.LstdFlags)

type Package interface {
	Name() string
	Init() error
	io.Closer
}

var Packages []Package

func Load(configFile string, packages ...Package) {
	debug.Nop()
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		Logger.Panicf("Error while reading config file %s: %s", configFile, err)
	}
	Packages = packages
	switch path.Ext(configFile) {
	case ".json":
		// Compact JSON to make it easier to extract JSON per package
		var buf bytes.Buffer
		err = json.Compact(&buf, data)
		if err != nil {
			Logger.Panicf("Error in JSON config file %s: %s", configFile, err)
		}
		data = buf.Bytes()

		// Unmarshal packages in given order
		for _, pkg := range packages {
			// Extract JSON only for this package
			key := []byte(`"` + pkg.Name() + `":{`)
			begin := bytes.Index(data, key)
			if begin == -1 {
				continue
			}
			begin += len(key) - 1
			end := 0
			braceCounter := 0
			for i := begin; i < len(data); i++ {
				switch data[i] {
				case '{':
					braceCounter++
				case '}':
					braceCounter--
				}
				if braceCounter == 0 {
					end = i + 1
					break
				}
			}

			err = json.Unmarshal(data[begin:end], pkg)
			if err != nil {
				Logger.Panicf("Error while unmarshalling JSON from config file %s: %s", configFile, err)
			}
			err := pkg.Init()
			if err != nil {
				Logger.Panicf("Error while initializing package %s: %s", pkg.Name(), err)
			}
		}

	default:
		panic("Unsupported config file: " + configFile)
	}
}

func Close() {
	for i := len(Packages) - 1; i >= 0; i-- {
		err := Packages[i].Close()
		if err != nil {
			Logger.Println("Error while closing package %s: %s", Packages[i].Name(), err)
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"os/user"
	"path/filepath"

	"github.com/gsamokovarov/jump/scoring"
)

const (
	defaultScoreFile  = "scores.json"
	defaultSearchFile = "search.json"
	defaultPinsFile   = "pins.json"
	defaultHomeDir    = ".jump"
	defaultXDGDir     = "jump"
)

// Config represents the config directory and all the miscellaneous
// configuration files we can have in there.
type Config interface {
	ReadEntries() (scoring.Entries, error)
	WriteEntries(scoring.Entries) error

	ReadSearch() Search
	WriteSearch(string, int) error

	ReadPins() (map[string]string, error)
	FindPin(string) (string, bool)
	WritePin(string, string) error
	RemovePin(string) error
}

type fileConfig struct {
	Dir    string
	Scores string
	Search string
	Pins   string
}

// Setup setups the config folder from a directory path.
//
// If the directories don't already exists, they are created and if the score
// file is present, it is loaded.
func Setup(dir string) (Config, error) {
	// We get the directory check for free form os.MkdirAll.
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}

	scores := filepath.Join(dir, defaultScoreFile)
	search := filepath.Join(dir, defaultSearchFile)
	pins := filepath.Join(dir, defaultPinsFile)

	return &fileConfig{dir, scores, search, pins}, nil
}

// SetupDefault setups the config folder from a directory path.
//
// If the directory path is an empty string, the path is automatically guessed.
func SetupDefault(dir string) (Config, error) {
	dir, err := findConfigDir(dir)
	if err != nil {
		return nil, err
	}

	return Setup(dir)
}

// findConfigDir finds the jump configuration directory.
//
// The search algorithm tries the directories in order:
//
// - $JUMP_HOME (if given)
// - $HOME/.jump (if already exists)
// - $XDG_CONFIG_HOME/jump (prefer for new installs)
//
// We're moving towards XDG, but for existing installs or non-XDG supported
// systems, the ~/.jump dur will be used.
func findConfigDir(dir string) (string, error) {
	if dir != "" {
		return dir, nil
	}

	home, err := homeDir()
	if err != nil {
		return dir, err
	}

	configDir := filepath.Join(home, "."+defaultHomeDir)
	if _, err := os.Stat(configDir); os.IsNotExist(err) {
		if xdgHome := os.Getenv("XDG_CONFIG_HOME"); xdgHome != "" {
			configDir = filepath.Join(xdgHome, defaultXDGDir)
		}
	}

	return configDir, nil
}

// See https://github.com/golang/go/issues/26463
func homeDir() (string, error) {
	home := os.Getenv("HOME")
	if home != "" {
		return home, nil
	}
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return usr.HomeDir, nil
}
<commit_msg>Fix the mistaken ~/..jump home dir<commit_after>package config

import (
	"os"
	"os/user"
	"path/filepath"

	"github.com/gsamokovarov/jump/scoring"
)

const (
	defaultScoreFile  = "scores.json"
	defaultSearchFile = "search.json"
	defaultPinsFile   = "pins.json"
	defaultHomeDir    = ".jump"
	defaultXDGDir     = "jump"
)

// Config represents the config directory and all the miscellaneous
// configuration files we can have in there.
type Config interface {
	ReadEntries() (scoring.Entries, error)
	WriteEntries(scoring.Entries) error

	ReadSearch() Search
	WriteSearch(string, int) error

	ReadPins() (map[string]string, error)
	FindPin(string) (string, bool)
	WritePin(string, string) error
	RemovePin(string) error
}

type fileConfig struct {
	Dir    string
	Scores string
	Search string
	Pins   string
}

// Setup setups the config folder from a directory path.
//
// If the directories don't already exists, they are created and if the score
// file is present, it is loaded.
func Setup(dir string) (Config, error) {
	// We get the directory check for free form os.MkdirAll.
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}

	scores := filepath.Join(dir, defaultScoreFile)
	search := filepath.Join(dir, defaultSearchFile)
	pins := filepath.Join(dir, defaultPinsFile)

	return &fileConfig{dir, scores, search, pins}, nil
}

// SetupDefault setups the config folder from a directory path.
//
// If the directory path is an empty string, the path is automatically guessed.
func SetupDefault(dir string) (Config, error) {
	dir, err := findConfigDir(dir)
	if err != nil {
		return nil, err
	}

	return Setup(dir)
}

// findConfigDir finds the jump configuration directory.
//
// The search algorithm tries the directories in order:
//
// - $JUMP_HOME (if given)
// - $HOME/.jump (if already exists)
// - $XDG_CONFIG_HOME/jump (prefer for new installs)
//
// We're moving towards XDG, but for existing installs or non-XDG supported
// systems, the ~/.jump dur will be used.
func findConfigDir(dir string) (string, error) {
	if dir != "" {
		return dir, nil
	}

	home, err := homeDir()
	if err != nil {
		return dir, err
	}

	configDir := filepath.Join(home, defaultHomeDir)
	if _, err := os.Stat(configDir); os.IsNotExist(err) {
		if xdgHome := os.Getenv("XDG_CONFIG_HOME"); xdgHome != "" {
			configDir = filepath.Join(xdgHome, defaultXDGDir)
		}
	}

	return configDir, nil
}

// See https://github.com/golang/go/issues/26463
func homeDir() (string, error) {
	home := os.Getenv("HOME")
	if home != "" {
		return home, nil
	}
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return usr.HomeDir, nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"time"

	"github.com/EscherAuth/escher/request"
	"github.com/EscherAuth/escher/utils"
)

type Interface interface {
	json.Unmarshaler
}

type Config struct {
	Date            string
	HashAlgo        string
	ApiSecret       string
	VendorKey       string
	AlgoPrefix      string
	AccessKeyId     string
	AuthHeaderName  string
	DateHeaderName  string
	CredentialScope string
}

func (c Config) ShortDate() string {
	return c.Date[:8]
}

func (c Config) Reconfig(date, hashAlgo, credentialScope, apiKeyID, apiSecret string) Config {
	return Config{
		HashAlgo:        hashAlgo,
		AccessKeyId:     apiKeyID,
		ApiSecret:       apiSecret,
		VendorKey:       c.VendorKey,
		AlgoPrefix:      c.AlgoPrefix,
		CredentialScope: credentialScope,
		AuthHeaderName:  c.AuthHeaderName,
		DateHeaderName:  c.DateHeaderName,
		Date:            date,
	}
}

func (c Config) ComposedAlgorithm() string {
	return c.GetAlgoPrefix() + "-HMAC-" + c.GetHashAlgo()
}

func (c Config) DateInEscherFormat() (string, error) {
	return c.GetDateWithFormat(utils.EscherDateFormat)
}

func (c Config) DateInHTTPHeaderFormat() (string, error) {
	return c.GetDateWithFormat(utils.HTTPHeaderFormat)
}

func (c Config) GetDateWithFormat(format string) (string, error) {

	if c.Date == "" {
		return time.Now().Format(format), nil
	}

	t, err := utils.ParseTime(c.Date)

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

	return t.Format(format), nil

}

func (c Config) GetHashAlgo() string {
	if c.HashAlgo != "" {
		return c.HashAlgo
	}

	return "SHA256"
}

func (c Config) GetAlgoPrefix() string {
	if c.AlgoPrefix != "" {
		return c.AlgoPrefix
	}

	return "ESR"
}

func (c Config) GetVendorKey() string {
	if c.VendorKey != "" {
		return c.VendorKey
	}

	return "Escher"
}

func (c Config) GetAuthHeaderName() string {
	if c.AuthHeaderName != "" {
		return c.AuthHeaderName
	}

	return "X-Escher-Auth"
}

func (c Config) GetDateHeaderName() string {
	if c.DateHeaderName != "" {
		return c.DateHeaderName
	}

	return "X-Escher-Date"
}

func (c Config) IsSigningInQuery(r request.Interface) bool {

	requiredKeys := []string{
		c.QueryKeyFor("Algorithm"),
		c.QueryKeyFor("Credentials"),
		c.QueryKeyFor("Date"),
		c.QueryKeyFor("Expires"),
		c.QueryKeyFor("SignedHeaders"),
	}

	q := r.Query()
	for _, requiredKey := range requiredKeys {
		if !q.IsInclude(requiredKey) {
			return false
		}
	}

	return true
}

func (c Config) SignatureQueryKey() string {
	return c.QueryKeyFor("Signature")
}

func (c Config) QueryKeyFor(key string) string {
	return "X-" + c.GetVendorKey() + "-" + key
}
<commit_msg>hot fix legacy code that cause panic on empty string byte slice length<commit_after>package config

import (
	"encoding/json"
	"time"

	"github.com/EscherAuth/escher/request"
	"github.com/EscherAuth/escher/utils"
)

type Interface interface {
	json.Unmarshaler
}

type Config struct {
	Date            string
	HashAlgo        string
	ApiSecret       string
	VendorKey       string
	AlgoPrefix      string
	AccessKeyId     string
	AuthHeaderName  string
	DateHeaderName  string
	CredentialScope string
}

func (c Config) ShortDate() string {
	date, err := c.DateInEscherFormat()

	if err != nil {
		return ""
	}

	return date[:8]
}

func (c Config) Reconfig(date, hashAlgo, credentialScope, apiKeyID, apiSecret string) Config {
	return Config{
		HashAlgo:        hashAlgo,
		AccessKeyId:     apiKeyID,
		ApiSecret:       apiSecret,
		VendorKey:       c.VendorKey,
		AlgoPrefix:      c.AlgoPrefix,
		CredentialScope: credentialScope,
		AuthHeaderName:  c.AuthHeaderName,
		DateHeaderName:  c.DateHeaderName,
		Date:            date,
	}
}

func (c Config) ComposedAlgorithm() string {
	return c.GetAlgoPrefix() + "-HMAC-" + c.GetHashAlgo()
}

func (c Config) DateInEscherFormat() (string, error) {
	return c.GetDateWithFormat(utils.EscherDateFormat)
}

func (c Config) DateInHTTPHeaderFormat() (string, error) {
	return c.GetDateWithFormat(utils.HTTPHeaderFormat)
}

func (c Config) GetDateWithFormat(format string) (string, error) {

	if c.Date == "" {
		return time.Now().Format(format), nil
	}

	t, err := utils.ParseTime(c.Date)

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

	return t.Format(format), nil

}

func (c Config) GetHashAlgo() string {
	if c.HashAlgo != "" {
		return c.HashAlgo
	}

	return "SHA256"
}

func (c Config) GetAlgoPrefix() string {
	if c.AlgoPrefix != "" {
		return c.AlgoPrefix
	}

	return "ESR"
}

func (c Config) GetVendorKey() string {
	if c.VendorKey != "" {
		return c.VendorKey
	}

	return "Escher"
}

func (c Config) GetAuthHeaderName() string {
	if c.AuthHeaderName != "" {
		return c.AuthHeaderName
	}

	return "X-Escher-Auth"
}

func (c Config) GetDateHeaderName() string {
	if c.DateHeaderName != "" {
		return c.DateHeaderName
	}

	return "X-Escher-Date"
}

func (c Config) IsSigningInQuery(r request.Interface) bool {

	requiredKeys := []string{
		c.QueryKeyFor("Algorithm"),
		c.QueryKeyFor("Credentials"),
		c.QueryKeyFor("Date"),
		c.QueryKeyFor("Expires"),
		c.QueryKeyFor("SignedHeaders"),
	}

	q := r.Query()
	for _, requiredKey := range requiredKeys {
		if !q.IsInclude(requiredKey) {
			return false
		}
	}

	return true
}

func (c Config) SignatureQueryKey() string {
	return c.QueryKeyFor("Signature")
}

func (c Config) QueryKeyFor(key string) string {
	return "X-" + c.GetVendorKey() + "-" + key
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	flag "github.com/spf13/pflag"
	"github.com/tendermint/confer"
)

var app *confer.Config
var appMtx sync.Mutex

func App() *confer.Config {
	appMtx.Lock()
	defer appMtx.Unlock()
	if app == nil {
		Init("")
	}
	return app
}

func SetApp(a *confer.Config) {
	appMtx.Lock()
	defer appMtx.Unlock()
	app = a
}

// NOTE: If you change this, maybe also change initDefaults()
var defaultConfig = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

Moniker = "anonymous"
Network = "tendermint_testnet3"
ListenAddr = "0.0.0.0:46656"
# First node to connect to.  Command-line overridable.
SeedNode = ""
# Pool of seeds. Best to use these, and specify one on command line
# if needed to override
SeedNodes = ["navytoad.chaintest.net:46656", "whiteferret.chaintest.net:46656", "magentagriffin.chaintest.net:46656", "greensalamander.chaintest.net:46656", "blackshadow.chaintest.net:46656", "purpleanteater.chaintest.net:46656", "pinkpenguin.chaintest.net:46656", "polkapig.chaintest.net:46656", "128.199.230.153:8080"]

[DB]
# The only other available backend is "memdb"
Backend = "leveldb"
# Dir = "~/.tendermint/data"

[Log.Stdout]
Level = "info"

[RPC.HTTP]
# For the RPC API HTTP server.  Port required.
ListenAddr = "0.0.0.0:46657"

[Alert]
# TODO: Document options

[SMTP]
# TODO: Document options
`

var DefaultGenesis = `{
    "accounts": [
        {
            "address": "69988763FCF806AC35D1A2F9C4885B7DD7B0599C",
            "amount": 1049800000000000
        },
        {
            "address": "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB",
            "amount": 1049800000000000
        }
    ],
    "validators": [
        {
            "pub_key": [1, "323A31EB01877858592AB7D593E9447110AFCD3ACF280D60C4F8E7C04FACC955"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "69988763FCF806AC35D1A2F9C4885B7DD7B0599C",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "DD2206E8F889EED3ABAAECEB2D18962D062A887346241820493FFE3B1DEF255D"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "69988763FCF806AC35D1A2F9C4885B7DD7B0599C",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "1B3256A3754FC6AB01110C166199A2F619E2D76DB3EE751E376FE404AC9FDCFF"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "69988763FCF806AC35D1A2F9C4885B7DD7B0599C",
            		"amount":  50000000000
            	}
            ]
        },
        {
            "pub_key": [1, "62CF1048BAEBB4FFFF360D5E896E3F4EC72D03D55183596931ED14995D512926"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "69988763FCF806AC35D1A2F9C4885B7DD7B0599C",
            		"amount":  50000000000
            	}
            ]
        }
    ]
}`

// NOTE: If you change this, maybe also change defaultConfig
func initDefaults(rootDir string) {
	app.SetDefault("Moniker", "anonymous")
	app.SetDefault("Network", "tendermint_testnet0")
	app.SetDefault("ListenAddr", "0.0.0.0:46656")
	app.SetDefault("DB.Backend", "leveldb")
	app.SetDefault("DB.Dir", rootDir+"/data")
	app.SetDefault("Log.Stdout.Level", "info")
	app.SetDefault("RPC.HTTP.ListenAddr", "0.0.0.0:46657")

	app.SetDefault("GenesisFile", rootDir+"/genesis.json")
	app.SetDefault("AddrBookFile", rootDir+"/addrbook.json")
	app.SetDefault("PrivValidatorfile", rootDir+"/priv_validator.json")

	app.SetDefault("FastSync", false)
}

func Init(rootDir string) {

	// Get rootdir
	if rootDir == "" {
		rootDir = os.Getenv("TMROOT")
	}
	if rootDir == "" {
		rootDir = os.Getenv("HOME") + "/.tendermint"
	}
	configFile := path.Join(rootDir, "config.toml")
	genesisFile := path.Join(rootDir, "genesis.json")

	// Write default config file if missing.
	checkWriteFile(configFile, defaultConfig)
	checkWriteFile(genesisFile, DefaultGenesis)

	// Initialize Config
	app = confer.NewConfig()
	initDefaults(rootDir)
	paths := []string{configFile}
	if err := app.ReadPaths(paths...); err != nil {
		log.Warn("Error reading configuration", "paths", paths, "error", err)
	}

	// Confused?
	//app.Debug()
}

// Check if a file exists; if not, ensure the directory is made and write the file
func checkWriteFile(configFile, contents string) {
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		if strings.Index(configFile, "/") != -1 {
			err := os.MkdirAll(filepath.Dir(configFile), 0700)
			if err != nil {
				fmt.Printf("Could not create directory: %v", err)
				os.Exit(1)
			}
		}
		err := ioutil.WriteFile(configFile, []byte(contents), 0600)
		if err != nil {
			fmt.Printf("Could not write config file: %v", err)
			os.Exit(1)
		}
		fmt.Printf("Config file written to %v.\n", configFile)
	}
}

func ParseFlags(args []string) {
	var flags = flag.NewFlagSet("main", flag.ExitOnError)
	var printHelp = false

	// Declare flags
	flags.BoolVar(&printHelp, "help", false, "Print this help message.")
	flags.String("listen_addr", app.GetString("ListenAddr"), "Listen address. (0.0.0.0:0 means any interface, any port)")
	flags.String("seed_node", app.GetString("SeedNode"), "Address of seed nodes")
	flags.String("rpc_http_listen_addr", app.GetString("RPC.HTTP.ListenAddr"), "RPC listen address. Port required")
	flags.Bool("fast_sync", app.GetBool("FastSync"), "Fast blockchain syncing")
	flags.String("log_stdout_level", app.GetString("Log.Stdout.Level"), "Stdout log level")
	flags.Parse(args)
	if printHelp {
		flags.PrintDefaults()
		os.Exit(0)
	}

	// Merge parsed flag values onto app.
	app.BindPFlag("ListenAddr", flags.Lookup("listen_addr"))
	app.BindPFlag("SeedNode", flags.Lookup("seed_node"))
	app.BindPFlag("FastSync", flags.Lookup("fast_sync"))
	app.BindPFlag("RPC.HTTP.ListenAddr", flags.Lookup("rpc_http_listen_addr"))
	app.BindPFlag("Log.Stdout.Level", flags.Lookup("log_stdout_level"))

	// Confused?
	//app.Debug()
}
<commit_msg>update validator set<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	flag "github.com/spf13/pflag"
	"github.com/tendermint/confer"
)

var app *confer.Config
var appMtx sync.Mutex

func App() *confer.Config {
	appMtx.Lock()
	defer appMtx.Unlock()
	if app == nil {
		Init("")
	}
	return app
}

func SetApp(a *confer.Config) {
	appMtx.Lock()
	defer appMtx.Unlock()
	app = a
}

// NOTE: If you change this, maybe also change initDefaults()
var defaultConfig = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

Moniker = "anonymous"
Network = "tendermint_testnet3"
ListenAddr = "0.0.0.0:46656"
# First node to connect to.  Command-line overridable.
SeedNode = ""
# Pool of seeds. Best to use these, and specify one on command line
# if needed to override
SeedNodes = ["navytoad.chaintest.net:46656", "whiteferret.chaintest.net:46656", "magentagriffin.chaintest.net:46656", "greensalamander.chaintest.net:46656", "blackshadow.chaintest.net:46656", "purpleanteater.chaintest.net:46656", "pinkpenguin.chaintest.net:46656", "polkapig.chaintest.net:46656", "128.199.230.153:8080"]

[DB]
# The only other available backend is "memdb"
Backend = "leveldb"
# Dir = "~/.tendermint/data"

[Log.Stdout]
Level = "info"

[RPC.HTTP]
# For the RPC API HTTP server.  Port required.
ListenAddr = "0.0.0.0:46657"

[Alert]
# TODO: Document options

[SMTP]
# TODO: Document options
`

var DefaultGenesis = `{
    "accounts": [
        {
            "address": "02AE5F611B6453922859715177B9F1294571861B",
            "amount": 1049800000000000
        },
        {
            "address": "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB",
            "amount": 1049800000000000
        }
    ],
    "validators": [
        {
            "pub_key": [1, "F60966A2ACF4F6F47E7705854A3A6842748B31531751EC755EABC4F248145BC6"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "02AE5F611B6453922859715177B9F1294571861B",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "338B632269B2DAE077BCF5CC2F6BDDD843F3AF64C1762270482BB6D163F5E4FB"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "02AE5F611B6453922859715177B9F1294571861B",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "AFCCAF02A65546D2F728EB84853BD6041AB2CBE98655B5FE64BE67988CAB2F47"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "02AE5F611B6453922859715177B9F1294571861B",
            		"amount":  50000000000
            	}
            ]
        },
        {
            "pub_key": [1, "ECBB3FFD17B050B23E63A08CC7CB7A0195D2EB2AC4E632CCCC270E7FB35C6CEF"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "02AE5F611B6453922859715177B9F1294571861B",
            		"amount":  50000000000
            	}
            ]
        }
    ]
}`

// NOTE: If you change this, maybe also change defaultConfig
func initDefaults(rootDir string) {
	app.SetDefault("Moniker", "anonymous")
	app.SetDefault("Network", "tendermint_testnet0")
	app.SetDefault("ListenAddr", "0.0.0.0:46656")
	app.SetDefault("DB.Backend", "leveldb")
	app.SetDefault("DB.Dir", rootDir+"/data")
	app.SetDefault("Log.Stdout.Level", "info")
	app.SetDefault("RPC.HTTP.ListenAddr", "0.0.0.0:46657")

	app.SetDefault("GenesisFile", rootDir+"/genesis.json")
	app.SetDefault("AddrBookFile", rootDir+"/addrbook.json")
	app.SetDefault("PrivValidatorfile", rootDir+"/priv_validator.json")

	app.SetDefault("FastSync", false)
}

func Init(rootDir string) {

	// Get rootdir
	if rootDir == "" {
		rootDir = os.Getenv("TMROOT")
	}
	if rootDir == "" {
		rootDir = os.Getenv("HOME") + "/.tendermint"
	}
	configFile := path.Join(rootDir, "config.toml")
	genesisFile := path.Join(rootDir, "genesis.json")

	// Write default config file if missing.
	checkWriteFile(configFile, defaultConfig)
	checkWriteFile(genesisFile, DefaultGenesis)

	// Initialize Config
	app = confer.NewConfig()
	initDefaults(rootDir)
	paths := []string{configFile}
	if err := app.ReadPaths(paths...); err != nil {
		log.Warn("Error reading configuration", "paths", paths, "error", err)
	}

	// Confused?
	//app.Debug()
}

// Check if a file exists; if not, ensure the directory is made and write the file
func checkWriteFile(configFile, contents string) {
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		if strings.Index(configFile, "/") != -1 {
			err := os.MkdirAll(filepath.Dir(configFile), 0700)
			if err != nil {
				fmt.Printf("Could not create directory: %v", err)
				os.Exit(1)
			}
		}
		err := ioutil.WriteFile(configFile, []byte(contents), 0600)
		if err != nil {
			fmt.Printf("Could not write config file: %v", err)
			os.Exit(1)
		}
		fmt.Printf("Config file written to %v.\n", configFile)
	}
}

func ParseFlags(args []string) {
	var flags = flag.NewFlagSet("main", flag.ExitOnError)
	var printHelp = false

	// Declare flags
	flags.BoolVar(&printHelp, "help", false, "Print this help message.")
	flags.String("listen_addr", app.GetString("ListenAddr"), "Listen address. (0.0.0.0:0 means any interface, any port)")
	flags.String("seed_node", app.GetString("SeedNode"), "Address of seed nodes")
	flags.String("rpc_http_listen_addr", app.GetString("RPC.HTTP.ListenAddr"), "RPC listen address. Port required")
	flags.Bool("fast_sync", app.GetBool("FastSync"), "Fast blockchain syncing")
	flags.String("log_stdout_level", app.GetString("Log.Stdout.Level"), "Stdout log level")
	flags.Parse(args)
	if printHelp {
		flags.PrintDefaults()
		os.Exit(0)
	}

	// Merge parsed flag values onto app.
	app.BindPFlag("ListenAddr", flags.Lookup("listen_addr"))
	app.BindPFlag("SeedNode", flags.Lookup("seed_node"))
	app.BindPFlag("FastSync", flags.Lookup("fast_sync"))
	app.BindPFlag("RPC.HTTP.ListenAddr", flags.Lookup("rpc_http_listen_addr"))
	app.BindPFlag("Log.Stdout.Level", flags.Lookup("log_stdout_level"))

	// Confused?
	//app.Debug()
}
<|endoftext|>