2019-10-23 17:09:04 +00:00
package main
2019-10-24 19:20:49 +00:00
import (
2019-11-15 17:35:55 +00:00
"bytes"
2019-10-24 19:20:49 +00:00
"context"
2019-11-07 00:45:00 +00:00
"encoding/json"
2019-10-24 19:20:49 +00:00
"errors"
"fmt"
2019-11-21 00:38:12 +00:00
"io"
2019-11-15 17:35:55 +00:00
"io/ioutil"
2019-11-21 07:12:27 +00:00
"net/http"
2019-10-24 19:20:49 +00:00
"os"
"path/filepath"
2019-11-22 23:22:10 +00:00
"reflect"
2019-10-28 22:23:40 +00:00
"strconv"
2019-10-24 19:20:49 +00:00
"strings"
"time"
2019-10-28 22:23:40 +00:00
"github.com/fatih/color"
2019-10-24 19:20:49 +00:00
"github.com/influxdata/influxdb"
2019-11-21 07:12:27 +00:00
ihttp "github.com/influxdata/influxdb/http"
2019-11-21 00:38:12 +00:00
ierror "github.com/influxdata/influxdb/kit/errors"
2019-10-24 19:20:49 +00:00
"github.com/influxdata/influxdb/pkger"
2019-10-28 22:23:40 +00:00
"github.com/olekukonko/tablewriter"
2019-10-24 19:20:49 +00:00
"github.com/spf13/cobra"
input "github.com/tcnksm/go-input"
2019-11-15 17:35:55 +00:00
"gopkg.in/yaml.v3"
2019-10-24 19:20:49 +00:00
)
2019-12-07 00:23:09 +00:00
type pkgSVCFn func ( cliReq httpClientOpts , opts ... pkger . ServiceSetterFn ) ( pkger . SVC , error )
2019-11-18 18:50:45 +00:00
2019-11-21 23:10:39 +00:00
func cmdPkg ( svcFn pkgSVCFn , opts ... genericCLIOptfn ) * cobra . Command {
return newCmdPkgBuilder ( svcFn , opts ... ) . cmdPkg ( )
2019-11-15 17:35:55 +00:00
}
2019-11-21 23:10:39 +00:00
type cmdPkgBuilder struct {
genericCLIOpts
svcFn pkgSVCFn
2019-12-07 00:23:09 +00:00
applyReqLimit int
2019-11-21 23:10:39 +00:00
file string
hasColor bool
hasTableBorders bool
meta pkger . Metadata
orgID string
quiet bool
applyOpts struct {
2019-12-06 00:53:00 +00:00
force string
2019-11-21 23:10:39 +00:00
}
exportOpts struct {
resourceType string
buckets string
dashboards string
labels string
2019-12-05 00:17:35 +00:00
telegrafs string
2019-11-21 23:10:39 +00:00
variables string
}
2019-11-21 07:12:27 +00:00
}
2019-11-21 23:10:39 +00:00
func newCmdPkgBuilder ( svcFn pkgSVCFn , opts ... genericCLIOptfn ) * cmdPkgBuilder {
opt := genericCLIOpts {
in : os . Stdin ,
w : os . Stdout ,
}
for _ , o := range opts {
o ( & opt )
}
return & cmdPkgBuilder {
genericCLIOpts : opt ,
svcFn : svcFn ,
2019-10-24 19:20:49 +00:00
}
2019-11-21 23:10:39 +00:00
}
func ( b * cmdPkgBuilder ) cmdPkg ( ) * cobra . Command {
cmd := b . cmdPkgApply ( )
cmd . AddCommand (
b . cmdPkgNew ( ) ,
b . cmdPkgExport ( ) ,
2019-11-21 23:43:10 +00:00
b . cmdPkgSummary ( ) ,
2019-11-21 23:10:39 +00:00
b . cmdPkgValidate ( ) ,
)
return cmd
}
2019-10-24 19:20:49 +00:00
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) cmdPkgApply ( ) * cobra . Command {
cmd := b . newCmd ( "pkg" )
cmd . Short = "Apply a pkg to create resources"
cmd . Flags ( ) . StringVarP ( & b . file , "file" , "f" , "" , "Path to package file" )
2019-11-21 07:12:27 +00:00
cmd . MarkFlagFilename ( "file" , "yaml" , "yml" , "json" )
2019-11-21 23:10:39 +00:00
cmd . Flags ( ) . BoolVarP ( & b . quiet , "quiet" , "q" , false , "disable output printing" )
2019-12-07 00:45:49 +00:00
cmd . Flags ( ) . IntVarP ( & b . applyReqLimit , "req-limit" , "r" , 0 , "Request limit for applying a pkg, defaults to 5(recommended for OSS)." )
2019-12-07 00:23:09 +00:00
cmd . Flags ( ) . StringVar ( & b . applyOpts . force , "force" , "" , ` TTY input, if package will have destructive changes, proceed if set "true". ` )
2019-10-24 19:20:49 +00:00
2019-11-21 23:10:39 +00:00
cmd . Flags ( ) . StringVarP ( & b . orgID , "org-id" , "o" , "" , "The ID of the organization that owns the bucket" )
2019-10-24 19:20:49 +00:00
cmd . MarkFlagRequired ( "org-id" )
2019-11-21 23:10:39 +00:00
cmd . Flags ( ) . BoolVarP ( & b . hasColor , "color" , "c" , true , "Enable color in output, defaults true" )
cmd . Flags ( ) . BoolVar ( & b . hasTableBorders , "table-borders" , true , "Enable table borders, defaults true" )
2019-10-30 17:55:13 +00:00
2019-11-21 23:10:39 +00:00
cmd . RunE = b . pkgApplyRunEFn ( )
2019-10-24 19:20:49 +00:00
return cmd
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) pkgApplyRunEFn ( ) func ( * cobra . Command , [ ] string ) error {
2019-10-24 19:20:49 +00:00
return func ( cmd * cobra . Command , args [ ] string ) ( e error ) {
2019-11-21 23:10:39 +00:00
color . NoColor = ! b . hasColor
2019-10-30 17:55:13 +00:00
2019-11-21 23:10:39 +00:00
influxOrgID , err := influxdb . IDFromString ( b . orgID )
2019-10-25 21:39:38 +00:00
if err != nil {
2019-12-04 01:00:15 +00:00
return fmt . Errorf ( "invalid org ID provided: %s" , err . Error ( ) )
2019-10-25 21:39:38 +00:00
}
2019-12-07 00:23:09 +00:00
svc , err := b . svcFn ( flags . httpClientOpts ( ) , pkger . WithApplyReqLimit ( b . applyReqLimit ) )
2019-10-24 19:20:49 +00:00
if err != nil {
return err
}
2019-11-21 23:10:39 +00:00
pkg , isTTY , err := b . readPkgStdInOrFile ( b . file )
2019-10-24 19:20:49 +00:00
if err != nil {
return err
}
2019-10-28 22:23:40 +00:00
_ , diff , err := svc . DryRun ( context . Background ( ) , * influxOrgID , pkg )
if err != nil {
return err
}
2019-11-21 23:10:39 +00:00
if ! b . quiet {
b . printPkgDiff ( diff )
2019-11-21 07:12:27 +00:00
}
2019-10-24 19:20:49 +00:00
2019-12-06 00:53:00 +00:00
isForced , _ := strconv . ParseBool ( b . applyOpts . force )
if ! isTTY && ! isForced && b . applyOpts . force != "conflict" {
2019-11-21 07:12:27 +00:00
ui := & input . UI {
Writer : os . Stdout ,
Reader : os . Stdin ,
}
confirm := getInput ( ui , "Confirm application of the above resources (y/n)" , "n" )
if strings . ToLower ( confirm ) != "y" {
fmt . Fprintln ( os . Stdout , "aborted application of package" )
return nil
}
2019-10-24 19:20:49 +00:00
}
2019-12-06 00:53:00 +00:00
if b . applyOpts . force != "conflict" && isTTY && diff . HasConflicts ( ) {
2019-11-21 07:12:27 +00:00
return errors . New ( "package has conflicts with existing resources and cannot safely apply" )
2019-10-24 19:20:49 +00:00
}
summary , err := svc . Apply ( context . Background ( ) , * influxOrgID , pkg )
if err != nil {
return err
}
2019-11-21 23:10:39 +00:00
if ! b . quiet {
b . printPkgSummary ( summary )
2019-11-21 07:12:27 +00:00
}
2019-10-24 19:20:49 +00:00
return nil
}
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) cmdPkgNew ( ) * cobra . Command {
cmd := b . newCmd ( "new" )
cmd . Short = "Create a reusable pkg to create resources in a declarative manner"
2019-11-15 17:35:55 +00:00
2019-11-21 23:10:39 +00:00
cmd . Flags ( ) . StringVarP ( & b . file , "file" , "f" , "" , "output file for created pkg; defaults to std out if no file provided; the extension of provided file (.yml/.json) will dictate encoding" )
cmd . Flags ( ) . BoolVarP ( & b . quiet , "quiet" , "q" , false , "skip interactive mode" )
cmd . Flags ( ) . StringVarP ( & b . meta . Name , "name" , "n" , "" , "name for new pkg" )
cmd . Flags ( ) . StringVarP ( & b . meta . Description , "description" , "d" , "" , "description for new pkg" )
cmd . Flags ( ) . StringVarP ( & b . meta . Version , "version" , "v" , "" , "version for new pkg" )
2019-11-15 17:35:55 +00:00
2019-11-21 23:10:39 +00:00
cmd . RunE = b . pkgNewRunEFn ( )
2019-11-15 17:35:55 +00:00
return cmd
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) pkgNewRunEFn ( ) func ( * cobra . Command , [ ] string ) error {
2019-11-15 17:35:55 +00:00
return func ( cmd * cobra . Command , args [ ] string ) error {
2019-11-21 23:10:39 +00:00
if ! b . quiet {
2019-11-15 17:35:55 +00:00
ui := & input . UI {
2019-11-21 23:10:39 +00:00
Writer : b . w ,
Reader : b . in ,
2019-11-15 17:35:55 +00:00
}
2019-11-21 23:10:39 +00:00
if b . meta . Name == "" {
b . meta . Name = getInput ( ui , "pkg name" , "" )
2019-11-15 17:35:55 +00:00
}
2019-11-21 23:10:39 +00:00
if b . meta . Description == "" {
b . meta . Description = getInput ( ui , "pkg description" , "" )
2019-11-15 17:35:55 +00:00
}
2019-11-21 23:10:39 +00:00
if b . meta . Version == "" {
b . meta . Version = getInput ( ui , "pkg version" , "" )
2019-11-15 17:35:55 +00:00
}
}
2019-11-21 23:10:39 +00:00
pkgSVC , err := b . svcFn ( flags . httpClientOpts ( ) )
2019-11-18 18:50:45 +00:00
if err != nil {
return err
}
2019-11-21 23:10:39 +00:00
return b . writePkg ( cmd . OutOrStdout ( ) , pkgSVC , b . file , pkger . CreateWithMetadata ( b . meta ) )
2019-11-21 00:38:12 +00:00
}
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) cmdPkgExport ( ) * cobra . Command {
cmd := b . newCmd ( "export" )
cmd . Short = "Export existing resources as a package"
cmd . AddCommand ( b . cmdPkgExportAll ( ) )
cmd . Flags ( ) . StringVarP ( & b . file , "file" , "f" , "" , "output file for created pkg; defaults to std out if no file provided; the extension of provided file (.yml/.json) will dictate encoding" )
cmd . Flags ( ) . StringVarP ( & b . meta . Name , "name" , "n" , "" , "name for new pkg" )
cmd . Flags ( ) . StringVarP ( & b . meta . Description , "description" , "d" , "" , "description for new pkg" )
cmd . Flags ( ) . StringVarP ( & b . meta . Version , "version" , "v" , "" , "version for new pkg" )
cmd . Flags ( ) . StringVar ( & b . exportOpts . resourceType , "resource-type" , "" , "The resource type provided will be associated with all IDs via stdin." )
cmd . Flags ( ) . StringVar ( & b . exportOpts . buckets , "buckets" , "" , "List of bucket ids comma separated" )
cmd . Flags ( ) . StringVar ( & b . exportOpts . dashboards , "dashboards" , "" , "List of dashboard ids comma separated" )
cmd . Flags ( ) . StringVar ( & b . exportOpts . labels , "labels" , "" , "List of label ids comma separated" )
2019-12-05 00:17:35 +00:00
cmd . Flags ( ) . StringVar ( & b . exportOpts . telegrafs , "telegraf-configs" , "" , "List of telegraf config ids comma separated" )
2019-11-21 23:10:39 +00:00
cmd . Flags ( ) . StringVar ( & b . exportOpts . variables , "variables" , "" , "List of variable ids comma separated" )
cmd . RunE = b . pkgExportRunEFn ( )
2019-11-21 00:38:12 +00:00
return cmd
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) pkgExportRunEFn ( ) func ( * cobra . Command , [ ] string ) error {
2019-11-21 00:38:12 +00:00
return func ( cmd * cobra . Command , args [ ] string ) error {
2019-11-21 23:10:39 +00:00
pkgSVC , err := b . svcFn ( flags . httpClientOpts ( ) )
2019-11-21 00:38:12 +00:00
if err != nil {
return err
}
2019-11-21 23:10:39 +00:00
opts := [ ] pkger . CreatePkgSetFn { pkger . CreateWithMetadata ( b . meta ) }
2019-11-21 00:38:12 +00:00
resTypes := [ ] struct {
kind pkger . Kind
idStrs [ ] string
} {
2019-11-21 23:10:39 +00:00
{ kind : pkger . KindBucket , idStrs : strings . Split ( b . exportOpts . buckets , "," ) } ,
{ kind : pkger . KindDashboard , idStrs : strings . Split ( b . exportOpts . dashboards , "," ) } ,
{ kind : pkger . KindLabel , idStrs : strings . Split ( b . exportOpts . labels , "," ) } ,
2019-12-05 00:17:35 +00:00
{ kind : pkger . KindTelegraf , idStrs : strings . Split ( b . exportOpts . telegrafs , "," ) } ,
2019-11-21 23:10:39 +00:00
{ kind : pkger . KindVariable , idStrs : strings . Split ( b . exportOpts . variables , "," ) } ,
2019-11-21 00:38:12 +00:00
}
for _ , rt := range resTypes {
2019-11-21 23:10:39 +00:00
newOpt , err := newResourcesToClone ( rt . kind , rt . idStrs )
2019-11-21 00:38:12 +00:00
if err != nil {
return ierror . Wrap ( err , rt . kind . String ( ) )
}
opts = append ( opts , newOpt )
}
2019-11-21 23:10:39 +00:00
if b . exportOpts . resourceType == "" {
return b . writePkg ( cmd . OutOrStdout ( ) , pkgSVC , b . file , opts ... )
2019-11-21 00:38:12 +00:00
}
2019-11-21 23:10:39 +00:00
kind := pkger . NewKind ( b . exportOpts . resourceType )
2019-11-21 00:38:12 +00:00
if err := kind . OK ( ) ; err != nil {
2019-11-21 23:10:39 +00:00
return errors . New ( "resource type must be one of bucket|dashboard|label|variable; got: " + b . exportOpts . resourceType )
2019-11-21 00:38:12 +00:00
}
2019-11-21 23:10:39 +00:00
if stdin , err := b . inStdIn ( ) ; err == nil {
stdinInpt , _ := b . readLines ( stdin )
if len ( stdinInpt ) > 0 {
args = stdinInpt
}
2019-11-21 00:38:12 +00:00
}
2019-11-21 23:10:39 +00:00
resTypeOpt , err := newResourcesToClone ( kind , args )
2019-11-15 17:35:55 +00:00
if err != nil {
return err
}
2019-11-21 23:10:39 +00:00
return b . writePkg ( cmd . OutOrStdout ( ) , pkgSVC , b . file , append ( opts , resTypeOpt ) ... )
2019-11-21 00:38:12 +00:00
}
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) cmdPkgExportAll ( ) * cobra . Command {
cmd := b . newCmd ( "all" )
cmd . Short = "Export all existing resources for an organization as a package"
2019-11-21 00:38:12 +00:00
2019-11-21 23:10:39 +00:00
cmd . Flags ( ) . StringVarP ( & b . file , "file" , "f" , "" , "output file for created pkg; defaults to std out if no file provided; the extension of provided file (.yml/.json) will dictate encoding" )
cmd . Flags ( ) . StringVarP ( & b . orgID , "org-id" , "o" , "" , "organization id" )
cmd . Flags ( ) . StringVarP ( & b . meta . Name , "name" , "n" , "" , "name for new pkg" )
cmd . Flags ( ) . StringVarP ( & b . meta . Description , "description" , "d" , "" , "description for new pkg" )
cmd . Flags ( ) . StringVarP ( & b . meta . Version , "version" , "v" , "" , "version for new pkg" )
2019-11-21 00:38:12 +00:00
2019-11-21 23:10:39 +00:00
cmd . RunE = b . pkgExportAllRunEFn ( )
2019-11-21 00:38:12 +00:00
return cmd
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) pkgExportAllRunEFn ( ) func ( * cobra . Command , [ ] string ) error {
2019-11-21 00:38:12 +00:00
return func ( cmd * cobra . Command , args [ ] string ) error {
2019-11-21 23:10:39 +00:00
pkgSVC , err := b . svcFn ( flags . httpClientOpts ( ) )
2019-11-21 00:38:12 +00:00
if err != nil {
return err
2019-11-15 17:35:55 +00:00
}
2019-11-21 00:38:12 +00:00
2019-11-21 23:10:39 +00:00
opts := [ ] pkger . CreatePkgSetFn { pkger . CreateWithMetadata ( b . meta ) }
2019-11-21 00:38:12 +00:00
2019-11-21 23:10:39 +00:00
orgID , err := influxdb . IDFromString ( b . orgID )
2019-11-21 00:38:12 +00:00
if err != nil {
2019-11-15 17:35:55 +00:00
return err
}
2019-11-21 00:38:12 +00:00
opts = append ( opts , pkger . CreateWithAllOrgResources ( * orgID ) )
2019-11-21 23:10:39 +00:00
return b . writePkg ( cmd . OutOrStdout ( ) , pkgSVC , b . file , opts ... )
2019-11-21 00:38:12 +00:00
}
}
2019-11-21 23:43:10 +00:00
func ( b * cmdPkgBuilder ) cmdPkgSummary ( ) * cobra . Command {
cmd := b . newCmd ( "summary" )
cmd . Short = "Summarize the provided package"
cmd . Flags ( ) . StringVarP ( & b . file , "file" , "f" , "" , "input file for pkg; if none provided will use TTY input" )
cmd . Flags ( ) . BoolVarP ( & b . hasColor , "color" , "c" , true , "Enable color in output, defaults true" )
cmd . Flags ( ) . BoolVar ( & b . hasTableBorders , "table-borders" , true , "Enable table borders, defaults true" )
cmd . RunE = func ( cmd * cobra . Command , args [ ] string ) error {
pkg , _ , err := b . readPkgStdInOrFile ( b . file )
if err != nil {
return err
}
b . printPkgSummary ( pkg . Summary ( ) )
return nil
}
return cmd
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) cmdPkgValidate ( ) * cobra . Command {
cmd := b . newCmd ( "validate" )
cmd . Short = "Validate the provided package"
2019-11-21 21:12:15 +00:00
2019-11-21 23:43:10 +00:00
cmd . Flags ( ) . StringVarP ( & b . file , "file" , "f" , "" , "input file for pkg; if none provided will use TTY input" )
2019-11-21 21:12:15 +00:00
cmd . RunE = func ( cmd * cobra . Command , args [ ] string ) error {
2019-11-21 23:43:10 +00:00
pkg , _ , err := b . readPkgStdInOrFile ( b . file )
2019-11-21 21:12:15 +00:00
if err != nil {
return err
}
return pkg . Validate ( )
}
return cmd
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) writePkg ( w io . Writer , pkgSVC pkger . SVC , outPath string , opts ... pkger . CreatePkgSetFn ) error {
pkg , err := pkgSVC . CreatePkg ( context . Background ( ) , opts ... )
2019-11-21 07:12:27 +00:00
if err != nil {
2019-11-21 23:10:39 +00:00
return err
2019-11-21 07:12:27 +00:00
}
2019-11-21 23:10:39 +00:00
buf , err := createPkgBuf ( pkg , outPath )
if err != nil {
return err
2019-11-21 07:12:27 +00:00
}
2019-11-21 23:10:39 +00:00
if outPath == "" {
_ , err := io . Copy ( w , buf )
return err
}
return ioutil . WriteFile ( outPath , buf . Bytes ( ) , os . ModePerm )
2019-11-21 07:12:27 +00:00
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) readPkgStdInOrFile ( file string ) ( * pkger . Pkg , bool , error ) {
if file != "" {
pkg , err := pkgFromFile ( file )
return pkg , false , err
}
var isTTY bool
if _ , err := b . inStdIn ( ) ; err == nil {
isTTY = true
}
pkg , err := pkgFromReader ( b . in )
return pkg , isTTY , err
}
func ( b * cmdPkgBuilder ) inStdIn ( ) ( * os . File , error ) {
stdin , _ := b . in . ( * os . File )
if stdin != os . Stdin {
return nil , errors . New ( "input not stdIn" )
}
info , err := stdin . Stat ( )
if err != nil {
2019-11-21 07:12:27 +00:00
return nil , err
}
2019-11-21 23:10:39 +00:00
if ( info . Mode ( ) & os . ModeCharDevice ) == os . ModeCharDevice {
return nil , errors . New ( "input not stdIn" )
}
return stdin , nil
}
2019-11-21 07:12:27 +00:00
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) readLines ( r io . Reader ) ( [ ] string , error ) {
bb , err := ioutil . ReadAll ( r )
2019-11-21 07:12:27 +00:00
if err != nil {
return nil , err
}
var stdinInput [ ] string
2019-11-21 23:10:39 +00:00
for _ , bs := range bytes . Split ( bb , [ ] byte ( "\n" ) ) {
trimmed := bytes . TrimSpace ( bs )
2019-11-21 07:12:27 +00:00
if len ( trimmed ) == 0 {
continue
}
stdinInput = append ( stdinInput , string ( trimmed ) )
}
return stdinInput , nil
}
2019-11-21 23:10:39 +00:00
func newResourcesToClone ( kind pkger . Kind , idStrs [ ] string ) ( pkger . CreatePkgSetFn , error ) {
ids , err := toInfluxIDs ( idStrs )
if err != nil {
return nil , err
}
var resources [ ] pkger . ResourceToClone
for _ , id := range ids {
resources = append ( resources , pkger . ResourceToClone {
Kind : kind ,
ID : id ,
} )
}
return pkger . CreateWithExistingResources ( resources ... ) , nil
}
2019-11-21 07:12:27 +00:00
func toInfluxIDs ( args [ ] string ) ( [ ] influxdb . ID , error ) {
var (
ids [ ] influxdb . ID
errs [ ] string
)
for _ , arg := range args {
normedArg := strings . TrimSpace ( strings . ToLower ( arg ) )
if normedArg == "" {
continue
}
id , err := influxdb . IDFromString ( normedArg )
if err != nil {
errs = append ( errs , "arg must provide a valid 16 length ID; got: " + arg )
continue
}
ids = append ( ids , * id )
}
if len ( errs ) > 0 {
return nil , errors . New ( strings . Join ( errs , "\n\t" ) )
}
return ids , nil
2019-11-21 00:38:12 +00:00
}
func createPkgBuf ( pkg * pkger . Pkg , outPath string ) ( * bytes . Buffer , error ) {
var (
buf bytes . Buffer
enc interface {
Encode ( interface { } ) error
}
)
switch ext := filepath . Ext ( outPath ) ; ext {
case ".json" :
jsonEnc := json . NewEncoder ( & buf )
jsonEnc . SetIndent ( "" , "\t" )
enc = jsonEnc
default :
enc = yaml . NewEncoder ( & buf )
}
if err := enc . Encode ( pkg ) ; err != nil {
return nil , err
}
return & buf , nil
2019-11-18 18:50:45 +00:00
}
2019-11-07 00:45:00 +00:00
2019-12-07 00:23:09 +00:00
func newPkgerSVC ( cliReqOpts httpClientOpts , opts ... pkger . ServiceSetterFn ) ( pkger . SVC , error ) {
2019-12-07 18:54:03 +00:00
httpClient , err := newHTTPClient ( )
2019-12-07 00:23:09 +00:00
if err != nil {
return nil , err
}
2019-11-07 00:45:00 +00:00
return pkger . NewService (
2019-12-07 00:23:09 +00:00
append ( opts ,
2019-12-07 18:54:03 +00:00
pkger . WithBucketSVC ( & ihttp . BucketService { Client : httpClient } ) ,
pkger . WithDashboardSVC ( & ihttp . DashboardService { Client : httpClient } ) ,
pkger . WithLabelSVC ( & ihttp . LabelService { Client : httpClient } ) ,
pkger . WithTelegrafSVC ( ihttp . NewTelegrafService ( httpClient ) ) ,
pkger . WithVariableSVC ( & ihttp . VariableService { Client : httpClient } ) ,
2019-12-07 00:23:09 +00:00
) ... ,
2019-11-07 00:45:00 +00:00
) , nil
2019-10-30 21:13:42 +00:00
}
2019-11-21 23:10:39 +00:00
func pkgFromReader ( stdin io . Reader ) ( * pkger . Pkg , error ) {
2019-11-21 07:12:27 +00:00
b , err := ioutil . ReadAll ( stdin )
if err != nil {
return nil , err
}
var enc pkger . Encoding
switch http . DetectContentType ( b [ 0 : 512 ] ) {
case "application/json" :
enc = pkger . EncodingJSON
default :
enc = pkger . EncodingYAML
}
return pkger . Parse ( enc , pkger . FromString ( string ( b ) ) )
}
2019-10-24 19:20:49 +00:00
func pkgFromFile ( path string ) ( * pkger . Pkg , error ) {
var enc pkger . Encoding
switch ext := filepath . Ext ( path ) ; ext {
case ".yaml" , ".yml" :
enc = pkger . EncodingYAML
case ".json" :
enc = pkger . EncodingJSON
default :
return nil , errors . New ( "file provided must be one of yaml/yml/json extension but got: " + ext )
}
return pkger . Parse ( enc , pkger . FromFile ( path ) )
}
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) printPkgDiff ( diff pkger . Diff ) {
2019-11-22 23:22:10 +00:00
red := color . New ( color . FgRed ) . SprintFunc ( )
green := color . New ( color . FgHiGreen , color . Bold ) . SprintFunc ( )
2019-10-28 22:23:40 +00:00
2019-11-22 23:22:10 +00:00
diffLn := func ( isNew bool , old , new interface { } ) string {
2019-10-28 22:23:40 +00:00
if isNew {
return green ( new )
2019-10-26 02:11:47 +00:00
}
2019-11-22 23:22:10 +00:00
if reflect . DeepEqual ( old , new ) {
return fmt . Sprint ( new )
2019-10-28 22:23:40 +00:00
}
2019-11-22 23:22:10 +00:00
return fmt . Sprintf ( "%s\n%s" , red ( old ) , green ( new ) )
2019-10-28 22:23:40 +00:00
}
boolDiff := func ( b bool ) string {
bb := strconv . FormatBool ( b )
if b {
return green ( bb )
}
return bb
}
2019-11-21 23:10:39 +00:00
tablePrintFn := b . tablePrinterGen ( )
2019-10-30 21:13:42 +00:00
if labels := diff . Labels ; len ( labels ) > 0 {
2019-10-28 22:23:40 +00:00
headers := [ ] string { "New" , "ID" , "Name" , "Color" , "Description" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "LABELS" , headers , len ( labels ) , func ( i int ) [ ] string {
l := labels [ i ]
var old pkger . DiffLabelValues
if l . Old != nil {
old = * l . Old
}
return [ ] string {
boolDiff ( l . IsNew ( ) ) ,
l . ID . String ( ) ,
l . Name ,
diffLn ( l . IsNew ( ) , old . Color , l . New . Color ) ,
diffLn ( l . IsNew ( ) , old . Description , l . New . Description ) ,
2019-10-26 02:11:47 +00:00
}
2019-10-28 22:23:40 +00:00
} )
}
2019-10-30 21:13:42 +00:00
if bkts := diff . Buckets ; len ( bkts ) > 0 {
2019-10-28 22:23:40 +00:00
headers := [ ] string { "New" , "ID" , "Name" , "Retention Period" , "Description" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "BUCKETS" , headers , len ( bkts ) , func ( i int ) [ ] string {
b := bkts [ i ]
var old pkger . DiffBucketValues
if b . Old != nil {
old = * b . Old
}
return [ ] string {
boolDiff ( b . IsNew ( ) ) ,
b . ID . String ( ) ,
b . Name ,
diffLn ( b . IsNew ( ) , old . RetentionRules . RP ( ) . String ( ) , b . New . RetentionRules . RP ( ) . String ( ) ) ,
diffLn ( b . IsNew ( ) , old . Description , b . New . Description ) ,
2019-10-26 02:11:47 +00:00
}
2019-10-28 22:23:40 +00:00
} )
2019-10-25 21:39:38 +00:00
}
2019-10-30 21:13:42 +00:00
if dashes := diff . Dashboards ; len ( dashes ) > 0 {
2019-11-01 18:11:42 +00:00
headers := [ ] string { "New" , "Name" , "Description" , "Num Charts" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "DASHBOARDS" , headers , len ( dashes ) , func ( i int ) [ ] string {
d := dashes [ i ]
return [ ] string {
boolDiff ( true ) ,
d . Name ,
green ( d . Desc ) ,
green ( strconv . Itoa ( len ( d . Charts ) ) ) ,
2019-10-30 21:13:42 +00:00
}
} )
}
2019-11-07 00:45:00 +00:00
if vars := diff . Variables ; len ( vars ) > 0 {
headers := [ ] string { "New" , "ID" , "Name" , "Description" , "Arg Type" , "Arg Values" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "VARIABLES" , headers , len ( vars ) , func ( i int ) [ ] string {
v := vars [ i ]
var old pkger . DiffVariableValues
if v . Old != nil {
old = * v . Old
}
var oldArgType string
if old . Args != nil {
oldArgType = old . Args . Type
}
var newArgType string
if v . New . Args != nil {
newArgType = v . New . Args . Type
}
return [ ] string {
boolDiff ( v . IsNew ( ) ) ,
v . ID . String ( ) ,
v . Name ,
diffLn ( v . IsNew ( ) , old . Description , v . New . Description ) ,
diffLn ( v . IsNew ( ) , oldArgType , newArgType ) ,
diffLn ( v . IsNew ( ) , printVarArgs ( old . Args ) , printVarArgs ( v . New . Args ) ) ,
2019-11-07 00:45:00 +00:00
}
} )
}
2019-12-04 01:00:15 +00:00
if teles := diff . Telegrafs ; len ( diff . Telegrafs ) > 0 {
headers := [ ] string { "New" , "Name" , "Description" }
tablePrintFn ( "TELEGRAF CONFIGS" , headers , len ( teles ) , func ( i int ) [ ] string {
t := teles [ i ]
return [ ] string {
boolDiff ( true ) ,
t . Name ,
green ( t . Description ) ,
}
} )
}
2019-10-28 22:23:40 +00:00
if len ( diff . LabelMappings ) > 0 {
headers := [ ] string { "New" , "Resource Type" , "Resource Name" , "Resource ID" , "Label Name" , "Label ID" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "LABEL MAPPINGS" , headers , len ( diff . LabelMappings ) , func ( i int ) [ ] string {
m := diff . LabelMappings [ i ]
return [ ] string {
boolDiff ( m . IsNew ) ,
string ( m . ResType ) ,
m . ResName ,
m . ResID . String ( ) ,
m . LabelName ,
m . LabelID . String ( ) ,
2019-10-26 02:11:47 +00:00
}
2019-10-28 22:23:40 +00:00
} )
}
}
2019-10-26 02:11:47 +00:00
2019-11-21 23:10:39 +00:00
func ( b * cmdPkgBuilder ) printPkgSummary ( sum pkger . Summary ) {
tablePrintFn := b . tablePrinterGen ( )
2019-10-28 22:23:40 +00:00
if labels := sum . Labels ; len ( labels ) > 0 {
headers := [ ] string { "ID" , "Name" , "Description" , "Color" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "LABELS" , headers , len ( labels ) , func ( i int ) [ ] string {
l := labels [ i ]
return [ ] string {
l . ID . String ( ) ,
l . Name ,
l . Properties [ "description" ] ,
l . Properties [ "color" ] ,
2019-10-26 02:11:47 +00:00
}
2019-10-28 22:23:40 +00:00
} )
}
if buckets := sum . Buckets ; len ( buckets ) > 0 {
headers := [ ] string { "ID" , "Name" , "Retention" , "Description" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "BUCKETS" , headers , len ( buckets ) , func ( i int ) [ ] string {
bucket := buckets [ i ]
return [ ] string {
bucket . ID . String ( ) ,
bucket . Name ,
formatDuration ( bucket . RetentionPeriod ) ,
bucket . Description ,
2019-10-26 02:11:47 +00:00
}
2019-10-28 22:23:40 +00:00
} )
}
2019-10-30 21:13:42 +00:00
if dashes := sum . Dashboards ; len ( dashes ) > 0 {
headers := [ ] string { "ID" , "Name" , "Description" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "DASHBOARDS" , headers , len ( dashes ) , func ( i int ) [ ] string {
d := dashes [ i ]
return [ ] string { d . ID . String ( ) , d . Name , d . Description }
2019-10-30 21:13:42 +00:00
} )
}
2019-11-07 00:45:00 +00:00
if vars := sum . Variables ; len ( vars ) > 0 {
headers := [ ] string { "ID" , "Name" , "Description" , "Arg Type" , "Arg Values" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "VARIABLES" , headers , len ( vars ) , func ( i int ) [ ] string {
v := vars [ i ]
args := v . Arguments
return [ ] string {
v . ID . String ( ) ,
v . Name ,
v . Description ,
args . Type ,
printVarArgs ( args ) ,
2019-11-07 00:45:00 +00:00
}
} )
}
2019-12-04 01:00:15 +00:00
if teles := sum . TelegrafConfigs ; len ( teles ) > 0 {
headers := [ ] string { "ID" , "Name" , "Description" }
tablePrintFn ( "TELEGRAF CONFIGS" , headers , len ( teles ) , func ( i int ) [ ] string {
t := teles [ i ]
return [ ] string {
t . ID . String ( ) ,
t . Name ,
t . Description ,
}
} )
}
2019-10-28 22:23:40 +00:00
if mappings := sum . LabelMappings ; len ( mappings ) > 0 {
headers := [ ] string { "Resource Type" , "Resource Name" , "Resource ID" , "Label Name" , "Label ID" }
2019-11-22 23:22:10 +00:00
tablePrintFn ( "LABEL MAPPINGS" , headers , len ( mappings ) , func ( i int ) [ ] string {
m := mappings [ i ]
return [ ] string {
string ( m . ResourceType ) ,
m . ResourceName ,
m . ResourceID . String ( ) ,
m . LabelName ,
m . LabelID . String ( ) ,
2019-10-28 22:23:40 +00:00
}
} )
}
}
2019-11-22 23:22:10 +00:00
func ( b * cmdPkgBuilder ) tablePrinterGen ( ) func ( table string , headers [ ] string , count int , rowFn func ( i int ) [ ] string ) {
return func ( table string , headers [ ] string , count int , rowFn func ( i int ) [ ] string ) {
tablePrinter ( b . w , table , headers , count , b . hasColor , b . hasTableBorders , rowFn )
2019-11-07 00:45:00 +00:00
}
}
2019-11-22 23:22:10 +00:00
func tablePrinter ( wr io . Writer , table string , headers [ ] string , count int , hasColor , hasTableBorders bool , rowFn func ( i int ) [ ] string ) {
color . New ( color . FgYellow , color . Bold ) . Fprintln ( os . Stdout , strings . ToUpper ( table ) )
2019-10-28 22:23:40 +00:00
2019-11-21 23:10:39 +00:00
w := tablewriter . NewWriter ( wr )
2019-10-30 17:55:13 +00:00
w . SetBorder ( hasTableBorders )
w . SetRowLine ( hasTableBorders )
2019-11-01 18:11:42 +00:00
var alignments [ ] int
for range headers {
alignments = append ( alignments , tablewriter . ALIGN_CENTER )
}
2019-11-22 23:22:10 +00:00
descrCol := find ( "description" , headers )
2019-10-28 22:23:40 +00:00
if descrCol != - 1 {
w . SetColMinWidth ( descrCol , 30 )
2019-11-01 18:11:42 +00:00
alignments [ descrCol ] = tablewriter . ALIGN_LEFT
2019-10-28 22:23:40 +00:00
}
w . SetHeader ( headers )
2019-11-01 18:11:42 +00:00
w . SetColumnAlignment ( alignments )
2019-10-28 22:23:40 +00:00
2019-11-22 23:22:10 +00:00
for i := range make ( [ ] struct { } , count ) {
w . Append ( rowFn ( i ) )
}
2019-10-28 22:23:40 +00:00
footers := make ( [ ] string , len ( headers ) )
footers [ len ( footers ) - 2 ] = "TOTAL"
footers [ len ( footers ) - 1 ] = strconv . Itoa ( count )
w . SetFooter ( footers )
2019-10-30 17:55:13 +00:00
if hasColor {
var colors [ ] tablewriter . Colors
for i := 0 ; i < len ( headers ) ; i ++ {
colors = append ( colors , tablewriter . Color ( tablewriter . FgHiCyanColor ) )
}
w . SetHeaderColor ( colors ... )
2019-11-01 18:11:42 +00:00
colors [ len ( colors ) - 2 ] = tablewriter . Color ( tablewriter . FgHiBlueColor )
colors [ len ( colors ) - 1 ] = tablewriter . Color ( tablewriter . FgHiBlueColor )
2019-10-30 17:55:13 +00:00
w . SetFooterColor ( colors ... )
}
2019-10-28 22:23:40 +00:00
w . Render ( )
fmt . Fprintln ( os . Stdout )
2019-10-24 19:20:49 +00:00
}
2019-11-21 23:10:39 +00:00
func printVarArgs ( a * influxdb . VariableArguments ) string {
if a == nil {
return "<nil>"
}
if a . Type == "map" {
b , err := json . Marshal ( a . Values )
if err != nil {
return "{}"
}
return string ( b )
}
if a . Type == "constant" {
vals , ok := a . Values . ( influxdb . VariableConstantValues )
if ! ok {
return "[]"
}
var out [ ] string
for _ , s := range vals {
out = append ( out , fmt . Sprintf ( "%q" , s ) )
}
return fmt . Sprintf ( "[%s]" , strings . Join ( out , " " ) )
}
if a . Type == "query" {
qVal , ok := a . Values . ( influxdb . VariableQueryValues )
if ! ok {
return ""
}
return fmt . Sprintf ( "language=%q query=%q" , qVal . Language , qVal . Query )
}
return "unknown variable argument"
}
2019-10-24 19:20:49 +00:00
func formatDuration ( d time . Duration ) string {
if d == 0 {
return "inf"
}
return d . String ( )
}
2019-11-22 23:22:10 +00:00
func find ( needle string , haystack [ ] string ) int {
for i , h := range haystack {
if strings . ToLower ( h ) == needle {
return i
}
}
return - 1
}