278 lines
6.2 KiB
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
|
|
}
|