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-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-11-18 18:50:45 +00:00
type pkgSVCFn func ( cliReq httpClientOpts ) ( pkger . SVC , error )
func pkgCmd ( newSVCFn pkgSVCFn ) * cobra . Command {
cmd := pkgApplyCmd ( newSVCFn )
2019-11-15 17:35:55 +00:00
cmd . AddCommand (
2019-11-21 00:38:12 +00:00
pkgNewCmd ( newSVCFn ) ,
pkgExportCmd ( newSVCFn ) ,
2019-11-15 17:35:55 +00:00
)
return cmd
}
2019-11-21 07:12:27 +00:00
type pkgApplyOpts struct {
orgID , file string
hasColor , hasTableBorders bool
quiet , forceOnConflict bool
}
2019-11-18 18:50:45 +00:00
func pkgApplyCmd ( newSVCFn pkgSVCFn ) * cobra . Command {
2019-10-24 19:20:49 +00:00
cmd := & cobra . Command {
Use : "pkg" ,
2019-11-15 17:35:55 +00:00
Short : "Apply a pkg to create resources" ,
2019-10-24 19:20:49 +00:00
}
2019-11-21 07:12:27 +00:00
var opts pkgApplyOpts
cmd . Flags ( ) . StringVarP ( & opts . file , "file" , "f" , "" , "Path to package file" )
cmd . MarkFlagFilename ( "file" , "yaml" , "yml" , "json" )
cmd . Flags ( ) . BoolVar ( & opts . forceOnConflict , "force-on-conflict" , true , "TTY input, if package will have destructive changes, proceed if set true." )
cmd . Flags ( ) . BoolVarP ( & opts . quiet , "quiet" , "q" , false , "disable output printing" )
2019-10-24 19:20:49 +00:00
2019-11-21 07:12:27 +00:00
cmd . Flags ( ) . StringVarP ( & opts . 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 07:12:27 +00:00
cmd . Flags ( ) . BoolVarP ( & opts . hasColor , "color" , "c" , true , "Enable color in output, defaults true" )
cmd . Flags ( ) . BoolVar ( & opts . hasTableBorders , "table-borders" , true , "Enable table borders, defaults true" )
2019-10-30 17:55:13 +00:00
2019-11-21 07:12:27 +00:00
cmd . RunE = pkgApplyRunEFn ( newSVCFn , & opts )
2019-10-24 19:20:49 +00:00
return cmd
}
2019-11-21 07:12:27 +00:00
func pkgApplyRunEFn ( newSVCFn pkgSVCFn , opts * pkgApplyOpts ) 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 07:12:27 +00:00
if ! opts . hasColor {
2019-10-30 17:55:13 +00:00
color . NoColor = true
}
2019-11-21 07:12:27 +00:00
influxOrgID , err := influxdb . IDFromString ( opts . orgID )
2019-10-25 21:39:38 +00:00
if err != nil {
return err
}
2019-11-18 18:50:45 +00:00
svc , err := newSVCFn ( flags . httpClientOpts ( ) )
2019-10-24 19:20:49 +00:00
if err != nil {
return err
}
2019-11-21 07:12:27 +00:00
var (
pkg * pkger . Pkg
isTTY bool
)
if stdin , _ := readStdIn ( ) ; stdin != nil {
isTTY = true
pkg , err = pkgFromStdIn ( stdin )
} else {
if opts . file == "" {
return errors . New ( "a file path is required when not using a TTY input" )
}
pkg , err = pkgFromFile ( opts . 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 07:12:27 +00:00
if ! opts . quiet {
printPkgDiff ( opts . hasColor , opts . hasTableBorders , diff )
}
2019-10-24 19:20:49 +00:00
2019-11-21 07:12:27 +00:00
if ! isTTY {
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-11-21 07:12:27 +00:00
if ! opts . forceOnConflict && isTTY && diff . HasConflicts ( ) {
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 07:12:27 +00:00
if ! opts . quiet {
printPkgSummary ( opts . hasColor , opts . hasTableBorders , summary )
}
2019-10-24 19:20:49 +00:00
return nil
}
}
2019-11-21 00:38:12 +00:00
func pkgNewCmd ( newSVCFn pkgSVCFn ) * cobra . Command {
2019-11-15 17:35:55 +00:00
cmd := & cobra . Command {
Use : "new" ,
Short : "Create a reusable pkg to create resources in a declarative manner" ,
}
2019-11-21 00:38:12 +00:00
var opts pkgNewOpts
cmd . Flags ( ) . StringVarP ( & opts . outPath , "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" )
2019-11-15 17:35:55 +00:00
cmd . Flags ( ) . BoolVarP ( & opts . quiet , "quiet" , "q" , false , "skip interactive mode" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Name , "name" , "n" , "" , "name for new pkg" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Description , "description" , "d" , "" , "description for new pkg" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Version , "version" , "v" , "" , "version for new pkg" )
2019-11-21 00:38:12 +00:00
cmd . RunE = pkgNewRunEFn ( newSVCFn , & opts )
2019-11-15 17:35:55 +00:00
return cmd
}
2019-11-21 00:38:12 +00:00
type pkgNewOpts struct {
2019-11-15 17:35:55 +00:00
quiet bool
outPath string
2019-11-21 00:38:12 +00:00
orgID string
2019-11-15 17:35:55 +00:00
meta pkger . Metadata
}
2019-11-21 00:38:12 +00:00
func pkgNewRunEFn ( newSVCFn pkgSVCFn , opt * pkgNewOpts ) func ( * cobra . Command , [ ] string ) error {
2019-11-15 17:35:55 +00:00
return func ( cmd * cobra . Command , args [ ] string ) error {
if ! opt . quiet {
ui := & input . UI {
Writer : os . Stdout ,
Reader : os . Stdin ,
}
if opt . meta . Name == "" {
2019-11-21 00:38:12 +00:00
opt . meta . Name = getInput ( ui , "pkg name" , "" )
2019-11-15 17:35:55 +00:00
}
if opt . meta . Description == "" {
2019-11-21 00:38:12 +00:00
opt . meta . Description = getInput ( ui , "pkg description" , opt . meta . Description )
2019-11-15 17:35:55 +00:00
}
if opt . meta . Version == "" {
2019-11-21 00:38:12 +00:00
opt . meta . Version = getInput ( ui , "pkg version" , opt . meta . Version )
2019-11-15 17:35:55 +00:00
}
}
2019-11-18 18:50:45 +00:00
pkgSVC , err := newSVCFn ( flags . httpClientOpts ( ) )
if err != nil {
return err
}
2019-11-21 00:38:12 +00:00
return writePkg ( cmd . OutOrStdout ( ) , pkgSVC , opt . outPath , pkger . CreateWithMetadata ( opt . meta ) )
}
}
type pkgExportOpts struct {
outPath string
meta pkger . Metadata
resourceType string
buckets string
dashboards string
labels string
variables string
}
func pkgExportCmd ( newSVCFn pkgSVCFn ) * cobra . Command {
cmd := & cobra . Command {
Use : "export" ,
Short : "Export existing resources as a package" ,
}
cmd . AddCommand ( pkgExportAllCmd ( newSVCFn ) )
var opts pkgExportOpts
cmd . Flags ( ) . StringVarP ( & opts . outPath , "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 ( & opts . meta . Name , "name" , "n" , "" , "name for new pkg" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Description , "description" , "d" , "" , "description for new pkg" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Version , "version" , "v" , "" , "version for new pkg" )
cmd . Flags ( ) . StringVar ( & opts . resourceType , "resource-type" , "" , "The resource type provided will be associated with all IDs via stdin." )
cmd . Flags ( ) . StringVar ( & opts . buckets , "buckets" , "" , "List of bucket ids comma separated" )
cmd . Flags ( ) . StringVar ( & opts . dashboards , "dashboards" , "" , "List of dashboard ids comma separated" )
cmd . Flags ( ) . StringVar ( & opts . labels , "labels" , "" , "List of label ids comma separated" )
cmd . Flags ( ) . StringVar ( & opts . variables , "variables" , "" , "List of variable ids comma separated" )
cmd . RunE = pkgExportRunEFn ( newSVCFn , & opts )
return cmd
}
func pkgExportRunEFn ( newSVCFn pkgSVCFn , cmdOpts * pkgExportOpts ) func ( * cobra . Command , [ ] string ) error {
return func ( cmd * cobra . Command , args [ ] string ) error {
pkgSVC , err := newSVCFn ( flags . httpClientOpts ( ) )
if err != nil {
return err
}
opts := [ ] pkger . CreatePkgSetFn { pkger . CreateWithMetadata ( cmdOpts . meta ) }
resTypes := [ ] struct {
kind pkger . Kind
idStrs [ ] string
} {
{ kind : pkger . KindBucket , idStrs : strings . Split ( cmdOpts . buckets , "," ) } ,
{ kind : pkger . KindDashboard , idStrs : strings . Split ( cmdOpts . dashboards , "," ) } ,
{ kind : pkger . KindLabel , idStrs : strings . Split ( cmdOpts . labels , "," ) } ,
{ kind : pkger . KindVariable , idStrs : strings . Split ( cmdOpts . variables , "," ) } ,
}
for _ , rt := range resTypes {
newOpt , err := getResourcesToClone ( rt . kind , rt . idStrs )
if err != nil {
return ierror . Wrap ( err , rt . kind . String ( ) )
}
opts = append ( opts , newOpt )
}
if cmdOpts . resourceType == "" {
return writePkg ( cmd . OutOrStdout ( ) , pkgSVC , cmdOpts . outPath , opts ... )
}
kind := pkger . NewKind ( cmdOpts . resourceType )
if err := kind . OK ( ) ; err != nil {
return errors . New ( "resource type must be one of bucket|dashboard|label|variable; got: " + cmdOpts . resourceType )
}
2019-11-21 07:12:27 +00:00
stdinInpt , _ := readStdInLines ( )
2019-11-21 00:38:12 +00:00
if len ( stdinInpt ) > 0 {
args = stdinInpt
}
resTypeOpt , err := getResourcesToClone ( kind , args )
2019-11-15 17:35:55 +00:00
if err != nil {
return err
}
2019-11-21 00:38:12 +00:00
return writePkg ( cmd . OutOrStdout ( ) , pkgSVC , cmdOpts . outPath , append ( opts , resTypeOpt ) ... )
}
}
func getResourcesToClone ( 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
}
func pkgExportAllCmd ( newSVCFn pkgSVCFn ) * cobra . Command {
cmd := & cobra . Command {
Use : "all" ,
Short : "Export all existing resources for an organization as a package" ,
}
var opts pkgNewOpts
cmd . Flags ( ) . StringVarP ( & opts . outPath , "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 ( & opts . orgID , "org-id" , "o" , "" , "organization id" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Name , "name" , "n" , "" , "name for new pkg" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Description , "description" , "d" , "" , "description for new pkg" )
cmd . Flags ( ) . StringVarP ( & opts . meta . Version , "version" , "v" , "" , "version for new pkg" )
cmd . RunE = pkgExportAllRunEFn ( newSVCFn , & opts )
return cmd
}
func pkgExportAllRunEFn ( newSVCFn pkgSVCFn , opt * pkgNewOpts ) func ( * cobra . Command , [ ] string ) error {
return func ( cmd * cobra . Command , args [ ] string ) error {
pkgSVC , err := newSVCFn ( flags . httpClientOpts ( ) )
if err != nil {
return err
2019-11-15 17:35:55 +00:00
}
2019-11-21 00:38:12 +00:00
opts := [ ] pkger . CreatePkgSetFn { pkger . CreateWithMetadata ( opt . meta ) }
orgID , err := influxdb . IDFromString ( opt . orgID )
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 ) )
return writePkg ( cmd . OutOrStdout ( ) , pkgSVC , opt . outPath , opts ... )
}
}
2019-11-21 07:12:27 +00:00
func readStdIn ( ) ( * os . File , error ) {
info , err := os . Stdin . Stat ( )
if err != nil {
return nil , err
}
if ( info . Mode ( ) & os . ModeCharDevice ) == os . ModeCharDevice {
return nil , nil
}
return os . Stdin , nil
}
func readStdInLines ( ) ( [ ] string , error ) {
r , err := readStdIn ( )
if err != nil || r == nil {
return nil , err
}
b , err := ioutil . ReadAll ( r )
if err != nil {
return nil , err
}
var stdinInput [ ] string
for _ , bb := range bytes . Split ( b , [ ] byte ( "\n" ) ) {
trimmed := bytes . TrimSpace ( bb )
if len ( trimmed ) == 0 {
continue
}
stdinInput = append ( stdinInput , string ( trimmed ) )
}
return stdinInput , nil
}
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 writePkg ( w io . Writer , pkgSVC pkger . SVC , outPath string , opts ... pkger . CreatePkgSetFn ) error {
pkg , err := pkgSVC . CreatePkg ( context . Background ( ) , opts ... )
if err != nil {
return err
}
buf , err := createPkgBuf ( pkg , outPath )
if err != nil {
return err
}
2019-11-15 17:35:55 +00:00
2019-11-21 00:38:12 +00:00
if outPath == "" {
_ , err := io . Copy ( w , buf )
return err
2019-11-15 17:35:55 +00:00
}
2019-11-21 00:38:12 +00:00
return ioutil . WriteFile ( outPath , buf . Bytes ( ) , os . ModePerm )
2019-11-15 17:35:55 +00:00
}
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-11-18 18:50:45 +00:00
func newPkgerSVC ( cliReqOpts httpClientOpts ) ( pkger . SVC , error ) {
2019-11-07 00:45:00 +00:00
return pkger . NewService (
2019-11-21 07:12:27 +00:00
pkger . WithBucketSVC ( & ihttp . BucketService {
2019-11-18 18:50:45 +00:00
Addr : cliReqOpts . addr ,
Token : cliReqOpts . token ,
InsecureSkipVerify : cliReqOpts . skipVerify ,
} ) ,
2019-11-21 07:12:27 +00:00
pkger . WithDashboardSVC ( & ihttp . DashboardService {
2019-11-18 18:50:45 +00:00
Addr : cliReqOpts . addr ,
Token : cliReqOpts . token ,
InsecureSkipVerify : cliReqOpts . skipVerify ,
} ) ,
2019-11-21 07:12:27 +00:00
pkger . WithLabelSVC ( & ihttp . LabelService {
2019-11-18 18:50:45 +00:00
Addr : cliReqOpts . addr ,
Token : cliReqOpts . token ,
InsecureSkipVerify : cliReqOpts . skipVerify ,
} ) ,
2019-11-21 07:12:27 +00:00
pkger . WithVariableSVC ( & ihttp . VariableService {
2019-11-18 18:50:45 +00:00
Addr : cliReqOpts . addr ,
Token : cliReqOpts . token ,
InsecureSkipVerify : cliReqOpts . skipVerify ,
} ) ,
2019-11-07 00:45:00 +00:00
) , nil
2019-10-30 21:13:42 +00:00
}
2019-11-21 07:12:27 +00:00
func pkgFromStdIn ( stdin * os . File ) ( * pkger . Pkg , error ) {
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-10-30 17:55:13 +00:00
func printPkgDiff ( hasColor , hasTableBorders bool , diff pkger . Diff ) {
2019-10-28 22:23:40 +00:00
red := color . New ( color . FgRed ) . SprintfFunc ( )
green := color . New ( color . FgHiGreen , color . Bold ) . SprintfFunc ( )
strDiff := func ( isNew bool , old , new string ) string {
if isNew {
return green ( new )
2019-10-26 02:11:47 +00:00
}
2019-10-28 22:23:40 +00:00
if old == new {
return new
}
return fmt . Sprintf ( "%s\n%s" , red ( "%q" , old ) , green ( "%q" , new ) )
}
boolDiff := func ( b bool ) string {
bb := strconv . FormatBool ( b )
if b {
return green ( bb )
}
return bb
}
durDiff := func ( isNew bool , oldDur , newDur time . Duration ) string {
o := oldDur . String ( )
if oldDur == 0 {
o = "inf"
}
n := newDur . String ( )
if newDur == 0 {
n = "inf"
}
if isNew {
return green ( n )
}
if oldDur == newDur {
return n
}
return fmt . Sprintf ( "%s\n%s" , red ( o ) , green ( n ) )
}
2019-11-07 00:45:00 +00:00
tablePrintFn := tablePrinterGen ( hasColor , hasTableBorders )
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-07 00:45:00 +00:00
tablePrintFn ( "LABELS" , headers , len ( labels ) , func ( w * tablewriter . Table ) {
2019-10-30 21:13:42 +00:00
for _ , l := range labels {
2019-10-28 22:23:40 +00:00
w . Append ( [ ] string {
boolDiff ( l . IsNew ( ) ) ,
l . ID . String ( ) ,
l . Name ,
strDiff ( l . IsNew ( ) , l . OldColor , l . NewColor ) ,
strDiff ( l . IsNew ( ) , l . OldDesc , l . NewDesc ) ,
} )
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-07 00:45:00 +00:00
tablePrintFn ( "BUCKETS" , headers , len ( bkts ) , func ( w * tablewriter . Table ) {
2019-10-30 21:13:42 +00:00
for _ , b := range bkts {
2019-10-28 22:23:40 +00:00
w . Append ( [ ] string {
boolDiff ( b . IsNew ( ) ) ,
b . ID . String ( ) ,
b . Name ,
durDiff ( b . IsNew ( ) , b . OldRetention , b . NewRetention ) ,
strDiff ( b . IsNew ( ) , b . OldDesc , b . NewDesc ) ,
} )
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-07 00:45:00 +00:00
tablePrintFn ( "DASHBOARDS" , headers , len ( dashes ) , func ( w * tablewriter . Table ) {
2019-10-30 21:13:42 +00:00
for _ , d := range dashes {
w . Append ( [ ] string {
2019-11-01 18:11:42 +00:00
boolDiff ( true ) ,
2019-10-30 21:13:42 +00:00
d . Name ,
2019-11-01 18:11:42 +00:00
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" }
tablePrintFn ( "VARIABLES" , headers , len ( vars ) , func ( w * tablewriter . Table ) {
for _ , v := range vars {
var oldArgType string
if v . OldArgs != nil {
oldArgType = v . OldArgs . Type
}
var newArgType string
if v . NewArgs != nil {
newArgType = v . NewArgs . Type
}
w . Append ( [ ] string {
boolDiff ( v . IsNew ( ) ) ,
v . ID . String ( ) ,
v . Name ,
strDiff ( v . IsNew ( ) , v . OldDesc , v . NewDesc ) ,
strDiff ( v . IsNew ( ) , oldArgType , newArgType ) ,
strDiff ( v . IsNew ( ) , printVarArgs ( v . OldArgs ) , printVarArgs ( v . NewArgs ) ) ,
} )
}
} )
}
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-07 00:45:00 +00:00
tablePrintFn ( "LABEL MAPPINGS" , headers , len ( diff . LabelMappings ) , func ( w * tablewriter . Table ) {
2019-10-28 22:23:40 +00:00
for _ , m := range diff . LabelMappings {
w . Append ( [ ] 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-07 00:45:00 +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-30 17:55:13 +00:00
func printPkgSummary ( hasColor , hasTableBorders bool , sum pkger . Summary ) {
2019-11-07 00:45:00 +00:00
tablePrintFn := tablePrinterGen ( hasColor , hasTableBorders )
2019-10-28 22:23:40 +00:00
if labels := sum . Labels ; len ( labels ) > 0 {
headers := [ ] string { "ID" , "Name" , "Description" , "Color" }
2019-11-07 00:45:00 +00:00
tablePrintFn ( "LABELS" , headers , len ( labels ) , func ( w * tablewriter . Table ) {
2019-10-28 22:23:40 +00:00
for _ , l := range labels {
w . Append ( [ ] 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-07 00:45:00 +00:00
tablePrintFn ( "BUCKETS" , headers , len ( buckets ) , func ( w * tablewriter . Table ) {
2019-10-28 22:23:40 +00:00
for _ , bucket := range buckets {
w . Append ( [ ] 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-07 00:45:00 +00:00
tablePrintFn ( "DASHBOARDS" , headers , len ( dashes ) , func ( w * tablewriter . Table ) {
2019-10-30 21:13:42 +00:00
for _ , d := range dashes {
w . Append ( [ ] string {
d . ID . String ( ) ,
d . Name ,
d . Description ,
} )
}
} )
}
2019-11-07 00:45:00 +00:00
if vars := sum . Variables ; len ( vars ) > 0 {
headers := [ ] string { "ID" , "Name" , "Description" , "Arg Type" , "Arg Values" }
tablePrintFn ( "VARIABLES" , headers , len ( vars ) , func ( w * tablewriter . Table ) {
for _ , v := range vars {
args := v . Arguments
w . Append ( [ ] string {
v . ID . String ( ) ,
v . Name ,
v . Description ,
args . Type ,
printVarArgs ( args ) ,
} )
}
} )
}
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-07 00:45:00 +00:00
tablePrintFn ( "LABEL MAPPINGS" , headers , len ( mappings ) , func ( w * tablewriter . Table ) {
2019-10-28 22:23:40 +00:00
for _ , m := range mappings {
w . Append ( [ ] string {
string ( m . ResourceType ) ,
m . ResourceName ,
m . ResourceID . String ( ) ,
m . LabelName ,
m . LabelID . String ( ) ,
} )
}
} )
}
}
2019-11-07 00:45:00 +00:00
func tablePrinterGen ( hasColor , hasTableBorder bool ) func ( table string , headers [ ] string , count int , appendFn func ( w * tablewriter . Table ) ) {
return func ( table string , headers [ ] string , count int , appendFn func ( w * tablewriter . Table ) ) {
tablePrinter ( table , headers , count , hasColor , hasTableBorder , appendFn )
}
}
2019-10-30 17:55:13 +00:00
func tablePrinter ( table string , headers [ ] string , count int , hasColor , hasTableBorders bool , appendFn func ( w * tablewriter . Table ) ) {
2019-10-28 22:23:40 +00:00
descrCol := - 1
for i , h := range headers {
if strings . ToLower ( h ) == "description" {
descrCol = i
break
2019-10-24 19:20:49 +00:00
}
}
2019-10-28 22:23:40 +00:00
w := tablewriter . NewWriter ( os . Stdout )
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-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
}
color . New ( color . FgYellow , color . Bold ) . Fprintln ( os . Stdout , strings . ToUpper ( table ) )
w . SetHeader ( headers )
2019-11-01 18:11:42 +00:00
w . SetColumnAlignment ( alignments )
2019-10-28 22:23:40 +00:00
appendFn ( w )
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
}
func formatDuration ( d time . Duration ) string {
if d == 0 {
return "inf"
}
return d . String ( )
}