2018-10-11 02:45:11 +00:00
package main
import (
"context"
"fmt"
"io"
"os"
"strings"
2019-01-08 00:37:16 +00:00
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/http"
"github.com/influxdata/influxdb/kit/signals"
"github.com/influxdata/influxdb/models"
"github.com/influxdata/influxdb/write"
2018-10-11 02:45:11 +00:00
"github.com/spf13/cobra"
)
2020-03-23 09:00:06 +00:00
const (
inputFormatCsv = "csv"
inputFormatLineProtocol = "lp"
)
2020-03-26 20:09:34 +00:00
type writeFlagsType struct {
2020-03-25 18:19:38 +00:00
org organization
2018-10-26 02:23:50 +00:00
BucketID string
Bucket string
Precision string
2020-03-23 09:00:06 +00:00
Format string
2020-03-24 07:21:07 +00:00
File string
2018-10-11 02:45:11 +00:00
}
2020-03-26 20:09:34 +00:00
var writeFlags writeFlagsType
2020-02-19 20:43:06 +00:00
func cmdWrite ( f * globalFlags , opt genericCLIOpts ) * cobra . Command {
2020-03-24 07:42:56 +00:00
cmd := opt . newCmd ( "write" , fluxWriteF , true )
2020-03-24 07:21:07 +00:00
cmd . Args = cobra . MaximumNArgs ( 1 )
2020-02-19 20:43:06 +00:00
cmd . Short = "Write points to InfluxDB"
2020-03-24 07:42:56 +00:00
cmd . Long = ` Write data to InfluxDB via stdin, or add an entire file specified with the -f flag `
2018-10-11 02:45:11 +00:00
2020-03-25 18:19:38 +00:00
writeFlags . org . register ( cmd , true )
2020-01-10 00:34:30 +00:00
opts := flagOpts {
{
DestP : & writeFlags . BucketID ,
Flag : "bucket-id" ,
Desc : "The ID of destination bucket" ,
Persistent : true ,
} ,
{
DestP : & writeFlags . Bucket ,
Flag : "bucket" ,
Short : 'b' ,
EnvVar : "BUCKET_NAME" ,
Desc : "The name of destination bucket" ,
Persistent : true ,
} ,
{
DestP : & writeFlags . Precision ,
Flag : "precision" ,
Short : 'p' ,
Default : "ns" ,
Desc : "Precision of the timestamps of the lines" ,
Persistent : true ,
} ,
2018-10-11 02:45:11 +00:00
}
2020-01-10 00:34:30 +00:00
opts . mustRegister ( cmd )
2020-03-25 17:43:50 +00:00
cmd . PersistentFlags ( ) . StringVar ( & writeFlags . Format , "format" , "" , "Input format, either lp (Line Protocol) or csv (Comma Separated Values). Defaults to lp unless '.csv' extension" )
cmd . PersistentFlags ( ) . StringVarP ( & writeFlags . File , "file" , "f" , "" , "The path to the file to import" )
2020-03-26 20:09:34 +00:00
cmdDryRun := opt . newCmd ( "dryrun" , fluxWriteDryrunF , false )
2020-03-25 17:43:50 +00:00
cmdDryRun . Args = cobra . MaximumNArgs ( 1 )
cmdDryRun . Short = "Write to stdout instead of InfluxDB"
cmdDryRun . Long = ` Write protocol lines to stdout instead of InfluxDB. Troubleshoot conversion from CSV to line protocol. `
cmd . AddCommand ( cmdDryRun )
2020-01-10 00:34:30 +00:00
return cmd
2018-10-11 02:45:11 +00:00
}
2020-03-26 20:09:34 +00:00
// createLineReader uses writeFlags and cli arguments to create a reader that produces line protocol
func ( writeFlags * writeFlagsType ) createLineReader ( args [ ] string ) ( r io . Reader , closer io . Closer , err error ) {
2020-03-24 07:42:56 +00:00
if len ( args ) > 0 && args [ 0 ] [ 0 ] == '@' {
2020-03-25 17:43:50 +00:00
// backward compatibility: @ in arg denotes a file
2020-03-24 07:21:07 +00:00
writeFlags . File = args [ 0 ] [ 1 : ]
}
if len ( writeFlags . File ) > 0 {
f , err := os . Open ( writeFlags . File )
2018-10-11 02:45:11 +00:00
if err != nil {
2020-03-26 20:09:34 +00:00
return nil , nil , fmt . Errorf ( "failed to open %q: %v" , writeFlags . File , err )
2018-10-11 02:45:11 +00:00
}
2020-03-26 20:09:34 +00:00
closer = f
2018-10-11 02:45:11 +00:00
r = f
2020-03-24 07:21:07 +00:00
if len ( writeFlags . Format ) == 0 && strings . HasSuffix ( writeFlags . File , ".csv" ) {
2020-03-23 09:00:06 +00:00
writeFlags . Format = inputFormatCsv
}
2020-03-24 07:42:56 +00:00
} else if len ( args ) == 0 || args [ 0 ] == "-" {
2020-03-25 17:43:50 +00:00
// backward compatibility: "-" also means stdin
2020-03-24 07:21:07 +00:00
r = os . Stdin
2018-10-11 02:45:11 +00:00
} else {
r = strings . NewReader ( args [ 0 ] )
}
2020-03-23 09:00:06 +00:00
// validate input format
if len ( writeFlags . Format ) > 0 && writeFlags . Format != inputFormatLineProtocol && writeFlags . Format != inputFormatCsv {
2020-03-26 20:09:34 +00:00
return nil , nil , fmt . Errorf ( "unsupported input format: %s" , writeFlags . Format )
2020-03-23 09:00:06 +00:00
}
2018-10-11 02:45:11 +00:00
2020-03-23 09:00:06 +00:00
if writeFlags . Format == inputFormatCsv {
r = write . CsvToProtocolLines ( r )
2018-10-11 02:45:11 +00:00
}
2020-03-26 20:09:34 +00:00
return r , closer , nil
}
func fluxWriteF ( cmd * cobra . Command , args [ ] string ) error {
// validate InfluxDB flags
if err := writeFlags . org . validOrgFlags ( & flags ) ; err != nil {
return err
}
2018-10-11 02:45:11 +00:00
2020-03-26 20:09:34 +00:00
if writeFlags . Bucket != "" && writeFlags . BucketID != "" {
return fmt . Errorf ( "please specify one of bucket or bucket-id" )
}
if ! models . ValidPrecision ( writeFlags . Precision ) {
return fmt . Errorf ( "invalid precision" )
}
bs , err := newBucketService ( )
if err != nil {
return err
}
var filter platform . BucketFilter
if writeFlags . BucketID != "" {
filter . ID , err = platform . IDFromString ( writeFlags . BucketID )
2020-03-23 09:00:06 +00:00
if err != nil {
2020-03-26 20:09:34 +00:00
return fmt . Errorf ( "failed to decode bucket-id: %v" , err )
2020-03-23 09:00:06 +00:00
}
2020-03-26 20:09:34 +00:00
}
if writeFlags . Bucket != "" {
filter . Name = & writeFlags . Bucket
}
if writeFlags . org . id != "" {
filter . OrganizationID , err = platform . IDFromString ( writeFlags . org . id )
if err != nil {
return fmt . Errorf ( "failed to decode org-id id: %v" , err )
2020-03-23 09:00:06 +00:00
}
2020-03-26 20:09:34 +00:00
}
if writeFlags . org . name != "" {
filter . Org = & writeFlags . org . name
}
2020-03-23 09:00:06 +00:00
2020-03-26 20:09:34 +00:00
ctx := context . Background ( )
buckets , n , err := bs . FindBuckets ( ctx , filter )
if err != nil {
return fmt . Errorf ( "failed to retrieve buckets: %v" , err )
}
if n == 0 {
if writeFlags . Bucket != "" {
return fmt . Errorf ( "bucket %q was not found" , writeFlags . Bucket )
2020-03-23 09:00:06 +00:00
}
2020-03-26 20:09:34 +00:00
if writeFlags . BucketID != "" {
return fmt . Errorf ( "bucket with id %q does not exist" , writeFlags . BucketID )
}
}
bucketID , orgID := buckets [ 0 ] . ID , buckets [ 0 ] . OrgID
// create line reader
r , closer , err := writeFlags . createLineReader ( args )
if closer != nil {
defer closer . Close ( )
}
if err != nil {
return err
}
// write to InfluxDB
s := write . Batcher {
Service : & http . WriteService {
Addr : flags . Host ,
Token : flags . Token ,
Precision : writeFlags . Precision ,
InsecureSkipVerify : flags . skipVerify ,
} ,
}
ctx = signals . WithStandardSignals ( ctx )
if err := s . Write ( ctx , orgID , bucketID , r ) ; err != nil && err != context . Canceled {
return fmt . Errorf ( "failed to write data: %v" , err )
2018-10-24 20:51:28 +00:00
}
2019-01-22 19:34:01 +00:00
2018-10-24 20:51:28 +00:00
return nil
2018-10-11 02:45:11 +00:00
}
2020-03-26 20:09:34 +00:00
func fluxWriteDryrunF ( cmd * cobra . Command , args [ ] string ) error {
// create line reader
r , closer , err := writeFlags . createLineReader ( args )
if closer != nil {
defer closer . Close ( )
}
if err != nil {
return err
}
// dry run
_ , err = io . Copy ( os . Stdout , r )
if err != nil {
return fmt . Errorf ( "failed: %v" , err )
}
return nil
}