Пример #1
0
func main() {
	flag.Parse()

	// Set up profiling, if requested.
	if cpuprofile != "" {
		log.Info("Profiling enabled")
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Errorf("Unable to create path: %s", err.Error())
		}
		defer closeFile(f)

		err = pprof.StartCPUProfile(f)
		if err != nil {
			log.Errorf("Unable to start CPU Profile: %s", err.Error())
		}

		defer pprof.StopCPUProfile()
	}

	// Set logging
	log.SetLevel(logLevel)
	if logFile != "stdout" {
		f := createFile(logFile)
		defer closeFile(f)

		log.Infof("Redirecting logging to %s", logFile)
		log.SetOutput(f)
	}

	// Set the data directory.
	if flag.NArg() == 0 {
		flag.Usage()
		println("Data path argument required")
		log.Error("No data path supplied -- aborting")
		os.Exit(1)
	}

	dataPath := flag.Arg(0)
	createDir(dataPath)

	s := server.NewServer(dataPath, dbfile, snapAfter, host, port)
	go func() {
		log.Error(s.ListenAndServe(join).Error())
	}()

	if !disableReporting {
		reportLaunch()
	}

	terminate := make(chan os.Signal, 1)
	signal.Notify(terminate, os.Interrupt)
	<-terminate
	log.Info("rqlite server stopped")
}
Пример #2
0
// Query runs the supplied query against the sqlite database. It returns a slice of
// RowResults.
func (db *DB) Query(query string) (RowResults, error) {
	if !strings.HasPrefix(strings.ToUpper(query), "SELECT ") {
		log.Warnf("Query \"%s\" may modify the database", query)
	}
	rows, err := db.dbConn.Query(query)
	if err != nil {
		log.Errorf("failed to execute SQLite query: %s", err.Error())
		return nil, err
	}
	defer func() {
		err = rows.Close()
		if err != nil {
			log.Errorf("failed to close rows: %s", err.Error())
		}
	}()

	results := make(RowResults, 0)

	columns, _ := rows.Columns()
	rawResult := make([][]byte, len(columns))
	dest := make([]interface{}, len(columns))
	for i := range rawResult {
		dest[i] = &rawResult[i] // Pointers to each string in the interface slice
	}

	for rows.Next() {
		err = rows.Scan(dest...)
		if err != nil {
			log.Errorf("failed to scan SQLite row: %s", err.Error())
			return nil, err
		}

		r := make(RowResult)
		for i, raw := range rawResult {
			if raw == nil {
				r[columns[i]] = "null"
			} else {
				r[columns[i]] = string(raw)
			}
		}
		results = append(results, r)
	}
	log.Debugf("Executed query successfully: %s", query)
	return results, nil
}
Пример #3
0
// Recovery restores the state of the database using the given data.
func (d *DbStateMachine) Recovery(b []byte) error {
	log.Tracef("Restoring database state to path: %s", d.dbpath)
	err := ioutil.WriteFile(d.dbpath, b, os.ModePerm)
	if err != nil {
		log.Errorf("Failed to recover state: %s", err.Error())
		return err
	}
	log.Tracef("Database restored successfully to %s", d.dbpath)
	return nil
}
Пример #4
0
// Save captures the state of the database. The caller must ensure that
// no transaction is taking place during this call.
//
// http://sqlite.org/howtocorrupt.html states it is safe to do this
// as long as no transaction is in progress.
func (d *DbStateMachine) Save() ([]byte, error) {
	log.Tracef("Capturing database state from path: %s", d.dbpath)
	b, err := ioutil.ReadFile(d.dbpath)
	if err != nil {
		log.Errorf("Failed to save state: %s", err.Error())
		return nil, err
	}
	log.Tracef("Database state successfully saved to %s", d.dbpath)
	return b, nil
}
Пример #5
0
func (s *Server) tableReadHandler(w http.ResponseWriter, req *http.Request) {
	log.Tracef("tableReadHandler for URL: %s", req.URL)
	s.metrics.queryReceived.Inc(1)

	var failures = make([]FailedSqlStmt, 0)
	vars := mux.Vars(req)
	tablename := vars["tablename"]
	log.Tracef("Getting data from table %s", tablename)
	startTime := time.Now()
	stmt := "Select * from " + tablename
	r, err := s.db.Query(stmt)
	if err != nil {
		log.Tracef("Bad SQL statement: %s", err.Error())
		s.metrics.queryFail.Inc(1)
		failures = append(failures, FailedSqlStmt{stmt, err.Error()})
	} else {
		s.metrics.querySuccess.Inc(1)
	}
	duration := time.Since(startTime)

	rr := QueryResponse{Failures: failures, Rows: r}
	if e, _ := isExplain(req); e {
		rr.Time = duration.String()
	}

	pretty, _ := isPretty(req)
	var b []byte
	if pretty {
		b, err = json.MarshalIndent(rr, "", "    ")
	} else {
		b, err = json.Marshal(rr)
	}
	if err != nil {
		log.Errorf("Failed to marshal JSON data: %s", err.Error())
		http.Error(w, err.Error(), http.StatusBadRequest) // Internal error actually
	} else {
		_, err = w.Write([]byte(b))
		if err != nil {
			log.Errorf("Error writting JSON data: %s", err.Error())
		}
	}
}
Пример #6
0
func reportLaunch() {
	json := fmt.Sprintf(`{"os": "%s", "arch": "%s", "app": "rqlite"}`, runtime.GOOS, runtime.GOARCH)
	data := bytes.NewBufferString(json)
	client := http.Client{Timeout: time.Duration(5 * time.Second)}
	go func() {
		_, err := client.Post("https://logs-01.loggly.com/inputs/8a0edd84-92ba-46e4-ada8-c529d0f105af/tag/reporting/",
			"application/json", data)
		if err != nil {
			log.Errorf("Report launch failed: %s", err.Error())
		}
	}()
}
Пример #7
0
func createFile(path string) *os.File {
	usr, _ := user.Current()
	dir := usr.HomeDir

	// Check in case of paths like "/something/~/something/"
	if path[:2] == "~/" {
		path = strings.Replace(path, "~/", dir+"/", 1)
	}

	if err := os.MkdirAll(filepath.Dir(path), 0744); err != nil {
		log.Errorf("Unable to create path: %s", err.Error())
		os.Exit(1)
	}

	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil && !strings.Contains(err.Error(), "is a directory") {
		log.Errorf("Unable to open file: %s", err.Error())
		os.Exit(1)
	}

	return f
}
Пример #8
0
// Apply executes a set of sqlite statements, within a transaction. All statements
// will take effect, or none.
func (c *TransactionExecuteCommandSet) Apply(server raft.Server) (interface{}, error) {
	log.Tracef("Applying TransactionExecuteCommandSet of size %d", len(c.Stmts))

	commitSuccess := false
	db := server.Context().(*db.DB)
	defer func() {
		if !commitSuccess {
			err := db.RollbackTransaction()
			if err != nil {
				log.Errorf("Failed to rollback transaction: %s", err.Error())
			}
		}
	}()

	err := db.StartTransaction()
	if err != nil {
		log.Errorf("Failed to start transaction: %s", err.Error())
		return nil, err
	}

	for i := range c.Stmts {
		err = db.Execute(c.Stmts[i])
		if err != nil {
			log.Errorf("Failed to execute statement within transaction: %s", err.Error())
			return nil, err
		}
	}

	if err = db.CommitTransaction(); err != nil {
		log.Errorf("Failed to commit transaction: %s", err.Error())
		return nil, err
	}

	commitSuccess = true
	return nil, nil
}
Пример #9
0
func (s *Server) writeHandler(w http.ResponseWriter, req *http.Request) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.raftServer.State() != "leader" {
		s.leaderRedirect(w, req)
		return
	}

	log.Tracef("writeHandler for URL: %s", req.URL)
	s.metrics.executeReceived.Inc(1)

	currentIndex := s.raftServer.CommitIndex()
	count := currentIndex - s.snapConf.lastIndex
	if uint64(count) > s.snapConf.snapshotAfter {
		log.Info("Committed log entries snapshot threshold reached, starting snapshot")
		err := s.raftServer.TakeSnapshot()
		s.logSnapshot(err, currentIndex, count)
		s.snapConf.lastIndex = currentIndex
		s.metrics.snapshotCreated.Inc(1)
	}

	// Read the value from the POST body.
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Tracef("Bad HTTP request: %s", err.Error())
		s.metrics.executeFail.Inc(1)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	stmts := strings.Split(string(b), ";")
	if stmts[len(stmts)-1] == "" {
		stmts = stmts[:len(stmts)-1]
	}

	log.Tracef("Execute statement contains %d commands", len(stmts))
	if len(stmts) == 0 {
		log.Trace("No database execute commands supplied")
		s.metrics.executeFail.Inc(1)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	transaction, _ := isTransaction(req)
	startTime := time.Now()
	failures, err := s.execute(transaction, stmts)
	if err != nil {
		log.Errorf("Database mutation failed: %s", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	duration := time.Since(startTime)

	wr := StmtResponse{Failures: failures}
	if e, _ := isExplain(req); e {
		wr.Time = duration.String()
	}

	pretty, _ := isPretty(req)
	if pretty {
		b, err = json.MarshalIndent(wr, "", "    ")
	} else {
		b, err = json.Marshal(wr)
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		_, err = w.Write([]byte(b))
		if err != nil {
			log.Errorf("Error writting JSON data: %s", err.Error())
		}
	}
}
Пример #10
0
// ListenAndServe starts the server.
func (s *Server) ListenAndServe(leader string) error {
	var err error

	log.Infof("Initializing Raft Server: %s", s.path)

	// Initialize and start Raft server.
	transporter := raft.NewHTTPTransporter("/raft", 200*time.Millisecond)
	stateMachine := NewDbStateMachine(s.dbPath)
	s.raftServer, err = raft.NewServer(s.name, s.path, transporter, stateMachine, s.db, "")
	if err != nil {
		log.Errorf("Failed to create new Raft server: %s", err.Error())
		return err
	}

	log.Info("Loading latest snapshot, if any, from disk")
	if err := s.raftServer.LoadSnapshot(); err != nil && os.IsNotExist(err) {
		log.Info("no snapshot found")
	} else if err != nil {
		log.Errorf("Error loading snapshot: %s", err.Error())
	}

	transporter.Install(s.raftServer, s)
	if err := s.raftServer.Start(); err != nil {
		log.Errorf("Error starting raft server: %s", err.Error())
	}

	if leader != "" {
		// Join to leader if specified.

		log.Infof("Attempting to join leader at %s", leader)

		if !s.raftServer.IsLogEmpty() {
			log.Error("Cannot join with an existing log")
			return errors.New("Cannot join with an existing log")
		}
		if err := s.Join(leader); err != nil {
			log.Errorf("Failed to join leader: %s", err.Error())
			return err
		}

	} else if s.raftServer.IsLogEmpty() {
		// Initialize the server by joining itself.

		log.Info("Initializing new cluster")

		_, err := s.raftServer.Do(&raft.DefaultJoinCommand{
			Name:             s.raftServer.Name(),
			ConnectionString: s.connectionString(),
		})
		if err != nil {
			log.Errorf("Failed to join to self: %s", err.Error())
		}

	} else {
		log.Info("Recovered from log")
	}

	log.Info("Initializing HTTP server")

	// Initialize and start HTTP server.
	s.httpServer = &http.Server{
		Addr:    fmt.Sprintf(":%d", s.port),
		Handler: s.router,
	}

	s.router.HandleFunc("/statistics", s.serveStatistics).Methods("GET")
	s.router.HandleFunc("/diagnostics", s.serveDiagnostics).Methods("GET")
	s.router.HandleFunc("/raft", s.serveRaftInfo).Methods("GET")
	s.router.HandleFunc("/db", s.readHandler).Methods("GET")
	s.router.HandleFunc("/db", s.writeHandler).Methods("POST")
	s.router.HandleFunc("/join", s.joinHandler).Methods("POST")

	log.Infof("Listening at %s", s.connectionString())

	return s.httpServer.ListenAndServe()
}
Пример #11
0
func createDir(path string) {
	if err := os.MkdirAll(path, 0744); err != nil {
		log.Errorf("Unable to create path: %s", err.Error())
		os.Exit(1)
	}
}
Пример #12
0
func closeFile(f *os.File) {
	if err := f.Close(); err != nil {
		log.Errorf("Unable to close file: %s", err.Error())
		os.Exit(1)
	}
}