github.com/cayleygraph/cayley@v0.7.7/cmd/cayley/cayley.go (about)

     1  // Copyright 2016 The Cayley Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !appengine
    16  
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"net/http"
    23  	_ "net/http/pprof"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	"github.com/cayleygraph/cayley/cmd/cayley/command"
    29  	"github.com/prometheus/client_golang/prometheus/promhttp"
    30  	"github.com/spf13/cobra"
    31  	"github.com/spf13/viper"
    32  
    33  	"github.com/cayleygraph/cayley/clog"
    34  	_ "github.com/cayleygraph/cayley/clog/glog"
    35  	"github.com/cayleygraph/cayley/graph"
    36  	"github.com/cayleygraph/cayley/version"
    37  	"github.com/cayleygraph/quad"
    38  
    39  	// Load supported backends
    40  	_ "github.com/cayleygraph/cayley/graph/all"
    41  
    42  	// Load all supported quad formats.
    43  	_ "github.com/cayleygraph/quad/dot"
    44  	_ "github.com/cayleygraph/quad/gml"
    45  	_ "github.com/cayleygraph/quad/graphml"
    46  	_ "github.com/cayleygraph/quad/json"
    47  	_ "github.com/cayleygraph/quad/jsonld"
    48  	_ "github.com/cayleygraph/quad/nquads"
    49  	_ "github.com/cayleygraph/quad/pquads"
    50  
    51  	// Load writer registry
    52  	_ "github.com/cayleygraph/cayley/writer"
    53  
    54  	// Load supported query languages
    55  	_ "github.com/cayleygraph/cayley/query/gizmo"
    56  	_ "github.com/cayleygraph/cayley/query/graphql"
    57  	_ "github.com/cayleygraph/cayley/query/mql"
    58  	_ "github.com/cayleygraph/cayley/query/sexp"
    59  )
    60  
    61  var (
    62  	rootCmd = &cobra.Command{
    63  		Use:   "cayley",
    64  		Short: "Cayley is a graph store and graph query layer.",
    65  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
    66  			clog.Infof("Cayley version: %s (%s)", version.Version, version.GitHash)
    67  			if conf, _ := cmd.Flags().GetString("config"); conf != "" {
    68  				viper.SetConfigFile(conf)
    69  			}
    70  			err := viper.ReadInConfig()
    71  			if _, ok := err.(viper.ConfigFileNotFoundError); !ok && err != nil {
    72  				return err
    73  			}
    74  			if conf := viper.ConfigFileUsed(); conf != "" {
    75  				wd, _ := os.Getwd()
    76  				if rel, _ := filepath.Rel(wd, conf); rel != "" && strings.Count(rel, "..") < 3 {
    77  					conf = rel
    78  				}
    79  				clog.Infof("using config file: %s", conf)
    80  			}
    81  			// force viper to load flags to variables
    82  			graph.IgnoreDuplicates = viper.GetBool("load.ignore_duplicates")
    83  			graph.IgnoreMissing = viper.GetBool("load.ignore_missing")
    84  			quad.DefaultBatch = viper.GetInt("load.batch")
    85  			if host, _ := cmd.Flags().GetString("pprof"); host != "" {
    86  				go func() {
    87  					if err := http.ListenAndServe(host, nil); err != nil {
    88  						clog.Errorf("failed to run pprof handler: %v", err)
    89  					}
    90  				}()
    91  			}
    92  			if host, _ := cmd.Flags().GetString("metrics"); host != "" {
    93  				go func() {
    94  					if err := http.ListenAndServe(host, promhttp.Handler()); err != nil {
    95  						clog.Errorf("failed to run metrics handler: %v", err)
    96  					}
    97  				}()
    98  			}
    99  			return nil
   100  		},
   101  	}
   102  	versionCmd = &cobra.Command{
   103  		Use:   "version",
   104  		Short: "Prints the version of Cayley.",
   105  		// do not execute any persistent actions
   106  		PersistentPreRun: func(cmd *cobra.Command, args []string) {},
   107  		Run: func(cmd *cobra.Command, args []string) {
   108  			fmt.Println("Cayley version:", version.Version)
   109  			fmt.Println("Git commit hash:", version.GitHash)
   110  			if version.BuildDate != "" {
   111  				fmt.Println("Build date:", version.BuildDate)
   112  			}
   113  		},
   114  	}
   115  )
   116  
   117  type pFlag struct {
   118  	flag.Value
   119  }
   120  
   121  func (pFlag) Type() string { return "string" }
   122  
   123  func init() {
   124  	// set config names and paths
   125  	viper.SetConfigName("cayley")
   126  	viper.SetEnvPrefix("cayley")
   127  	viper.AddConfigPath(".")
   128  	viper.AddConfigPath("$HOME/.cayley/")
   129  	viper.AddConfigPath("/etc/")
   130  	if conf := os.Getenv("CAYLEY_CFG"); conf != "" {
   131  		viper.SetConfigFile(conf)
   132  	}
   133  
   134  	rootCmd.AddCommand(
   135  		versionCmd,
   136  		command.NewInitDatabaseCmd(),
   137  		command.NewLoadDatabaseCmd(),
   138  		command.NewDumpDatabaseCmd(),
   139  		command.NewUpgradeCmd(),
   140  		command.NewReplCmd(),
   141  		command.NewQueryCmd(),
   142  		command.NewHttpCmd(),
   143  		command.NewConvertCmd(),
   144  		command.NewDedupCommand(),
   145  	)
   146  	rootCmd.PersistentFlags().StringP("config", "c", "", "path to an explicit configuration file")
   147  
   148  	qnames := graph.QuadStores()
   149  	rootCmd.PersistentFlags().StringP("db", "d", "memstore", "database backend to use: "+strings.Join(qnames, ", "))
   150  	rootCmd.PersistentFlags().StringP("dbpath", "a", "", "path or address string for database")
   151  	rootCmd.PersistentFlags().Bool("read_only", false, "open database in read-only mode")
   152  
   153  	rootCmd.PersistentFlags().Bool("dup", true, "don't stop loading on duplicated on add")
   154  	rootCmd.PersistentFlags().Bool("missing", false, "don't stop loading on missing key on delete")
   155  	rootCmd.PersistentFlags().Int("batch", quad.DefaultBatch, "size of quads batch to load at once")
   156  
   157  	rootCmd.PersistentFlags().String("memprofile", "", "path to output memory profile")
   158  	rootCmd.PersistentFlags().String("cpuprofile", "", "path to output cpu profile")
   159  
   160  	rootCmd.PersistentFlags().String("pprof", "", "host to serve pprof on (disabled by default)")
   161  	rootCmd.PersistentFlags().String("metrics", "", "host to serve metrics on (disabled by default)")
   162  
   163  	// bind flags to config variables
   164  	viper.BindPFlag(command.KeyBackend, rootCmd.PersistentFlags().Lookup("db"))
   165  	viper.BindPFlag(command.KeyAddress, rootCmd.PersistentFlags().Lookup("dbpath"))
   166  	viper.BindPFlag(command.KeyReadOnly, rootCmd.PersistentFlags().Lookup("read_only"))
   167  	viper.BindPFlag("load.ignore_duplicates", rootCmd.PersistentFlags().Lookup("dup"))
   168  	viper.BindPFlag("load.ignore_missing", rootCmd.PersistentFlags().Lookup("missing"))
   169  	viper.BindPFlag(command.KeyLoadBatch, rootCmd.PersistentFlags().Lookup("batch"))
   170  
   171  	// make both store.path and store.address work
   172  	viper.RegisterAlias(command.KeyPath, command.KeyAddress)
   173  	// aliases for legacy config files
   174  	viper.RegisterAlias("database", command.KeyBackend)
   175  	viper.RegisterAlias("db_path", command.KeyAddress)
   176  	viper.RegisterAlias("read_only", command.KeyReadOnly)
   177  	viper.RegisterAlias("db_options", command.KeyOptions)
   178  
   179  	{ // re-register standard Go flags to cobra
   180  		rf := rootCmd.PersistentFlags()
   181  		flag.CommandLine.VisitAll(func(f *flag.Flag) {
   182  			switch f.Name {
   183  			case "v": // glog.v
   184  				rf.VarP(pFlag{f.Value}, "verbose", "v", f.Usage)
   185  			case "vmodule": // glog.vmodule
   186  				rf.Var(pFlag{f.Value}, "vmodule", f.Usage)
   187  			case "log_backtrace_at": // glog.log_backtrace_at
   188  				rf.Var(pFlag{f.Value}, "backtrace", f.Usage)
   189  			case "stderrthreshold": // glog.stderrthreshold
   190  				rf.VarP(pFlag{f.Value}, "log", "l", f.Usage)
   191  			case "alsologtostderr": // glog.alsologtostderr
   192  				rf.Var(pFlag{f.Value}, f.Name, f.Usage)
   193  			case "logtostderr": // glog.logtostderr
   194  				f.Value.Set("true")
   195  				rf.Var(pFlag{f.Value}, f.Name, f.Usage)
   196  			case "log_dir": // glog.log_dir
   197  				rf.Var(pFlag{f.Value}, "logs", f.Usage)
   198  			}
   199  		})
   200  		// make sure flags parsed flag is set - parse empty args
   201  		flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError)
   202  		flag.CommandLine.Parse([]string{""})
   203  	}
   204  }
   205  
   206  func main() {
   207  	if err := rootCmd.Execute(); err != nil {
   208  		clog.Errorf("%v", err)
   209  		os.Exit(1)
   210  	}
   211  }