influxdb/cmd/influx/main.go

278 lines
6.2 KiB
Go

package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/bolt"
"github.com/influxdata/influxdb/cmd/influx/internal"
"github.com/influxdata/influxdb/http"
"github.com/influxdata/influxdb/internal/fs"
"github.com/influxdata/influxdb/kv"
"github.com/influxdata/influxdb/pkg/httpc"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.uber.org/zap"
)
const maxTCPConnections = 128
func main() {
influxCmd := influxCmd()
if err := influxCmd.Execute(); err != nil {
os.Exit(1)
}
}
var (
httpClient *httpc.Client
)
func newHTTPClient() (*httpc.Client, error) {
if httpClient != nil {
return httpClient, nil
}
c, err := http.NewHTTPClient(flags.host, flags.token, flags.skipVerify)
if err != nil {
return nil, err
}
httpClient = c
return httpClient, nil
}
type genericCLIOptfn func(*genericCLIOpts)
type genericCLIOpts struct {
in io.Reader
w io.Writer
}
func (o genericCLIOpts) newCmd(use string) *cobra.Command {
cmd := &cobra.Command{Use: use}
cmd.SetOutput(o.w)
return cmd
}
func in(r io.Reader) genericCLIOptfn {
return func(o *genericCLIOpts) {
o.in = r
}
}
func out(w io.Writer) genericCLIOptfn {
return func(o *genericCLIOpts) {
o.w = w
}
}
func influxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "influx",
Short: "Influx Client",
Run: func(cmd *cobra.Command, args []string) {
if err := checkSetup(flags.host); err != nil {
fmt.Printf("Note: %v\n", internal.ErrorFmt(err))
}
cmd.Usage()
},
}
viper.SetEnvPrefix("INFLUX")
cmd.AddCommand(
authCmd(),
bucketCmd,
deleteCmd,
organizationCmd(),
pingCmd,
cmdPkg(newPkgerSVC),
queryCmd,
transpileCmd,
replCmd,
setupCmd,
taskCmd,
userCmd(),
writeCmd,
)
cmd.PersistentFlags().StringVarP(&flags.token, "token", "t", "", "API token to be used throughout client calls")
viper.BindEnv("TOKEN")
if h := viper.GetString("TOKEN"); h != "" {
flags.token = h
} else if tok, err := getTokenFromDefaultPath(); err == nil {
flags.token = tok
}
cmd.PersistentFlags().StringVar(&flags.host, "host", "http://localhost:9999", "HTTP address of Influx")
viper.BindEnv("HOST")
if h := viper.GetString("HOST"); h != "" {
flags.host = h
}
cmd.PersistentFlags().BoolVar(&flags.local, "local", false, "Run commands locally against the filesystem")
cmd.PersistentFlags().BoolVar(&flags.skipVerify, "skip-verify", false, "SkipVerify controls whether a client verifies the server's certificate chain and host name.")
// Override help on all the commands tree
walk(cmd, func(c *cobra.Command) {
c.Flags().BoolP("help", "h", false, fmt.Sprintf("Help for the %s command ", c.Name()))
})
return cmd
}
// Flags contains all the CLI flag values for influx.
type Flags struct {
token string
host string
local bool
skipVerify bool
}
var flags Flags
func defaultTokenPath() (string, string, error) {
dir, err := fs.InfluxDir()
if err != nil {
return "", "", err
}
return filepath.Join(dir, "credentials"), dir, nil
}
func getTokenFromDefaultPath() (string, error) {
path, _, err := defaultTokenPath()
if err != nil {
return "", err
}
b, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
return string(b), nil
}
func writeTokenToPath(tok, path, dir string) error {
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
return ioutil.WriteFile(path, []byte(tok), 0600)
}
func checkSetup(host string) error {
s := &http.SetupService{
Addr: flags.host,
InsecureSkipVerify: flags.skipVerify,
}
isOnboarding, err := s.IsOnboarding(context.Background())
if err != nil {
return err
}
if isOnboarding {
return fmt.Errorf("the instance at %q has not been setup. Please run `influx setup` before issuing any additional commands", host)
}
return nil
}
func wrapCheckSetup(fn func(*cobra.Command, []string) error) func(*cobra.Command, []string) error {
return wrapErrorFmt(func(cmd *cobra.Command, args []string) error {
err := fn(cmd, args)
if err == nil {
return nil
}
if setupErr := checkSetup(flags.host); setupErr != nil && influxdb.EUnauthorized != influxdb.ErrorCode(setupErr) {
return setupErr
}
return err
})
}
func wrapErrorFmt(fn func(*cobra.Command, []string) error) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
err := fn(cmd, args)
if err == nil {
return nil
}
return internal.ErrorFmt(err)
}
}
// walk calls f for c and all of its children.
func walk(c *cobra.Command, f func(*cobra.Command)) {
f(c)
for _, c := range c.Commands() {
walk(c, f)
}
}
func newLocalKVService() (*kv.Service, error) {
boltFile, err := fs.BoltFile()
if err != nil {
return nil, err
}
store := bolt.NewKVStore(zap.NewNop(), boltFile)
if err := store.Open(context.Background()); err != nil {
return nil, err
}
return kv.NewService(zap.NewNop(), store), nil
}
type organization struct {
id, name string
}
func (org *organization) register(cmd *cobra.Command) {
cmd.Flags().StringVarP(&org.id, "org-id", "", "", "The ID of the organization that owns the bucket")
viper.BindEnv("ORG_ID")
if h := viper.GetString("ORG_ID"); h != "" {
org.id = h
}
cmd.Flags().StringVarP(&org.name, "org", "o", "", "The name of the organization that owns the bucket")
viper.BindEnv("ORG")
if h := viper.GetString("ORG"); h != "" {
org.name = h
}
}
func (org *organization) getID(orgSVC influxdb.OrganizationService) (influxdb.ID, error) {
if org.id != "" {
influxOrgID, err := influxdb.IDFromString(org.id)
if err != nil {
return 0, fmt.Errorf("invalid org ID provided: %s", err.Error())
}
return *influxOrgID, nil
} else if org.name != "" {
org, err := orgSVC.FindOrganization(context.Background(), influxdb.OrganizationFilter{
Name: &org.name,
})
if err != nil {
return 0, fmt.Errorf("%v", err)
}
return org.ID, nil
}
return 0, fmt.Errorf("failed to locate an organization id")
}
func (org *organization) validOrgFlags() error {
if org.id == "" && org.name == "" {
return fmt.Errorf("must specify org-id, or org name")
} else if org.id != "" && org.name != "" {
return fmt.Errorf("must specify org-id, or org name not both")
}
return nil
}