Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
b485821b
Commit
b485821b
authored
Sep 02, 2015
by
Brendan Burns
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Enable custom columns printing in kubectl
parent
1313e3b1
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
274 additions
and
0 deletions
+274
-0
custom_column_printer.go
pkg/kubectl/custom_column_printer.go
+95
-0
custom_column_printer_test.go
pkg/kubectl/custom_column_printer_test.go
+165
-0
resource_printer.go
pkg/kubectl/resource_printer.go
+14
-0
No files found.
pkg/kubectl/custom_column_printer.go
View file @
b485821b
...
@@ -17,6 +17,8 @@ limitations under the License.
...
@@ -17,6 +17,8 @@ limitations under the License.
package
kubectl
package
kubectl
import
(
import
(
"bufio"
"bytes"
"fmt"
"fmt"
"io"
"io"
"reflect"
"reflect"
...
@@ -35,6 +37,95 @@ const (
...
@@ -35,6 +37,95 @@ const (
flags
=
0
flags
=
0
)
)
// MassageJSONPath attempts to be flexible with JSONPath expressions, it accepts:
// * metadata.name (no leading '.' or curly brances '{...}'
// * {metadata.name} (no leading '.')
// * .metadata.name (no curly braces '{...}')
// * {.metadata.name} (complete expression)
// And transforms them all into a valid jsonpat expression:
// {.metadata.name}
func
massageJSONPath
(
pathExpression
string
)
string
{
if
len
(
pathExpression
)
==
0
{
return
pathExpression
}
if
pathExpression
[
0
]
!=
'{'
{
if
pathExpression
[
0
]
==
'.'
{
return
fmt
.
Sprintf
(
"{%s}"
,
pathExpression
)
}
else
{
return
fmt
.
Sprintf
(
"{.%s}"
,
pathExpression
)
}
}
else
{
if
pathExpression
[
1
]
==
'.'
{
return
pathExpression
}
else
{
return
fmt
.
Sprintf
(
"{.%s}"
,
pathExpression
[
1
:
len
(
pathExpression
)
-
1
])
}
}
}
// NewCustomColumnsPrinterFromSpec creates a custom columns printer from a comma separated list of <header>:<jsonpath-field-spec> pairs.
// e.g. NAME:metadata.name,API_VERSION:apiVersion creates a printer that prints:
//
// NAME API_VERSION
// foo bar
func
NewCustomColumnsPrinterFromSpec
(
spec
string
)
(
*
CustomColumnsPrinter
,
error
)
{
parts
:=
strings
.
Split
(
spec
,
","
)
if
len
(
parts
)
==
0
{
return
nil
,
fmt
.
Errorf
(
"custom-columns format specified but no custom columns given"
)
}
columns
:=
make
([]
Column
,
len
(
parts
))
for
ix
:=
range
parts
{
colSpec
:=
strings
.
Split
(
parts
[
ix
],
":"
)
if
len
(
colSpec
)
!=
2
{
return
nil
,
fmt
.
Errorf
(
"unexpected custom-columns spec: %s, expected <header>:<json-path-expr>"
,
parts
[
ix
])
}
columns
[
ix
]
=
Column
{
Header
:
colSpec
[
0
],
FieldSpec
:
massageJSONPath
(
colSpec
[
1
])}
}
return
&
CustomColumnsPrinter
{
Columns
:
columns
},
nil
}
func
splitOnWhitespace
(
line
string
)
[]
string
{
lineScanner
:=
bufio
.
NewScanner
(
bytes
.
NewBufferString
(
line
))
lineScanner
.
Split
(
bufio
.
ScanWords
)
result
:=
[]
string
{}
for
lineScanner
.
Scan
()
{
result
=
append
(
result
,
lineScanner
.
Text
())
}
return
result
}
// NewCustomColumnsPrinterFromTemplate creates a custom columns printer from a template stream. The template is expected
// to consist of two lines, whitespace separated. The first line is the header line, the second line is the jsonpath field spec
// For example the template below:
// NAME API_VERSION
// {metadata.name} {apiVersion}
func
NewCustomColumnsPrinterFromTemplate
(
templateReader
io
.
Reader
)
(
*
CustomColumnsPrinter
,
error
)
{
scanner
:=
bufio
.
NewScanner
(
templateReader
)
if
!
scanner
.
Scan
()
{
return
nil
,
fmt
.
Errorf
(
"invalid template, missing header line"
)
}
headers
:=
splitOnWhitespace
(
scanner
.
Text
())
if
!
scanner
.
Scan
()
{
return
nil
,
fmt
.
Errorf
(
"invalid template, missing spec line"
)
}
specs
:=
splitOnWhitespace
(
scanner
.
Text
())
fmt
.
Printf
(
"SPECS: %v"
,
specs
)
if
len
(
headers
)
!=
len
(
specs
)
{
return
nil
,
fmt
.
Errorf
(
"number of headers (%d) and field specifications (%d) don't match"
,
len
(
headers
),
len
(
specs
))
}
columns
:=
make
([]
Column
,
len
(
headers
))
for
ix
:=
range
headers
{
columns
[
ix
]
=
Column
{
Header
:
headers
[
ix
],
FieldSpec
:
massageJSONPath
(
specs
[
ix
]),
}
}
return
&
CustomColumnsPrinter
{
Columns
:
columns
},
nil
}
// Column represents a user specified column
// Column represents a user specified column
type
Column
struct
{
type
Column
struct
{
// The header to print above the column, general style is ALL_CAPS
// The header to print above the column, general style is ALL_CAPS
...
@@ -105,3 +196,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
...
@@ -105,3 +196,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
fmt
.
Fprintln
(
out
,
strings
.
Join
(
columns
,
"
\t
"
))
fmt
.
Fprintln
(
out
,
strings
.
Join
(
columns
,
"
\t
"
))
return
nil
return
nil
}
}
func
(
s
*
CustomColumnsPrinter
)
HandledResources
()
[]
string
{
return
[]
string
{}
}
pkg/kubectl/custom_column_printer_test.go
View file @
b485821b
...
@@ -18,12 +18,177 @@ package kubectl
...
@@ -18,12 +18,177 @@ package kubectl
import
(
import
(
"bytes"
"bytes"
"reflect"
"testing"
"testing"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime"
)
)
func
TestMassageJSONPath
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
input
string
expectedOutput
string
}{
{
input
:
"foo.bar"
,
expectedOutput
:
"{.foo.bar}"
},
{
input
:
"{foo.bar}"
,
expectedOutput
:
"{.foo.bar}"
},
{
input
:
".foo.bar"
,
expectedOutput
:
"{.foo.bar}"
},
{
input
:
"{.foo.bar}"
,
expectedOutput
:
"{.foo.bar}"
},
{
input
:
""
,
expectedOutput
:
""
},
}
for
_
,
test
:=
range
tests
{
output
:=
massageJSONPath
(
test
.
input
)
if
output
!=
test
.
expectedOutput
{
t
.
Errorf
(
"expected: %s, saw: %s"
,
test
.
expectedOutput
,
output
)
}
}
}
func
TestNewColumnPrinterFromSpec
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
spec
string
expectedColumns
[]
Column
expectErr
bool
name
string
}{
{
spec
:
""
,
expectErr
:
true
,
name
:
"empty"
,
},
{
spec
:
"invalid"
,
expectErr
:
true
,
name
:
"invalid1"
,
},
{
spec
:
"invalid=foobar"
,
expectErr
:
true
,
name
:
"invalid2"
,
},
{
spec
:
"invalid,foobar:blah"
,
expectErr
:
true
,
name
:
"invalid3"
,
},
{
spec
:
"NAME:metadata.name,API_VERSION:apiVersion"
,
name
:
"ok"
,
expectedColumns
:
[]
Column
{
{
Header
:
"NAME"
,
FieldSpec
:
"{.metadata.name}"
,
},
{
Header
:
"API_VERSION"
,
FieldSpec
:
"{.apiVersion}"
,
},
},
},
}
for
_
,
test
:=
range
tests
{
printer
,
err
:=
NewCustomColumnsPrinterFromSpec
(
test
.
spec
)
if
test
.
expectErr
{
if
err
==
nil
{
t
.
Errorf
(
"[%s] unexpected non-error"
,
test
.
name
)
}
continue
}
if
!
test
.
expectErr
&&
err
!=
nil
{
t
.
Errorf
(
"[%s] unexpected error: %v"
,
test
.
name
,
err
)
continue
}
if
!
reflect
.
DeepEqual
(
test
.
expectedColumns
,
printer
.
Columns
)
{
t
.
Errorf
(
"[%s]
\n
expected:
\n
%v
\n
saw:
\n
%v
\n
"
,
test
.
name
,
test
.
expectedColumns
,
printer
.
Columns
)
}
}
}
const
exampleTemplateOne
=
`NAME API_VERSION
{metadata.name} {apiVersion}`
const
exampleTemplateTwo
=
`NAME API_VERSION
{metadata.name} {apiVersion}`
func
TestNewColumnPrinterFromTemplate
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
spec
string
expectedColumns
[]
Column
expectErr
bool
name
string
}{
{
spec
:
""
,
expectErr
:
true
,
name
:
"empty"
,
},
{
spec
:
"invalid"
,
expectErr
:
true
,
name
:
"invalid1"
,
},
{
spec
:
"invalid=foobar"
,
expectErr
:
true
,
name
:
"invalid2"
,
},
{
spec
:
"invalid,foobar:blah"
,
expectErr
:
true
,
name
:
"invalid3"
,
},
{
spec
:
exampleTemplateOne
,
name
:
"ok"
,
expectedColumns
:
[]
Column
{
{
Header
:
"NAME"
,
FieldSpec
:
"{.metadata.name}"
,
},
{
Header
:
"API_VERSION"
,
FieldSpec
:
"{.apiVersion}"
,
},
},
},
{
spec
:
exampleTemplateTwo
,
name
:
"ok-2"
,
expectedColumns
:
[]
Column
{
{
Header
:
"NAME"
,
FieldSpec
:
"{.metadata.name}"
,
},
{
Header
:
"API_VERSION"
,
FieldSpec
:
"{.apiVersion}"
,
},
},
},
}
for
_
,
test
:=
range
tests
{
reader
:=
bytes
.
NewBufferString
(
test
.
spec
)
printer
,
err
:=
NewCustomColumnsPrinterFromTemplate
(
reader
)
if
test
.
expectErr
{
if
err
==
nil
{
t
.
Errorf
(
"[%s] unexpected non-error"
,
test
.
name
)
}
continue
}
if
!
test
.
expectErr
&&
err
!=
nil
{
t
.
Errorf
(
"[%s] unexpected error: %v"
,
test
.
name
,
err
)
continue
}
if
!
reflect
.
DeepEqual
(
test
.
expectedColumns
,
printer
.
Columns
)
{
t
.
Errorf
(
"[%s]
\n
expected:
\n
%v
\n
saw:
\n
%v
\n
"
,
test
.
name
,
test
.
expectedColumns
,
printer
.
Columns
)
}
}
}
func
TestColumnPrint
(
t
*
testing
.
T
)
{
func
TestColumnPrint
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
tests
:=
[]
struct
{
columns
[]
Column
columns
[]
Column
...
...
pkg/kubectl/resource_printer.go
View file @
b485821b
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
"fmt"
"fmt"
"io"
"io"
"io/ioutil"
"io/ioutil"
"os"
"reflect"
"reflect"
"sort"
"sort"
"strings"
"strings"
...
@@ -98,6 +99,19 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
...
@@ -98,6 +99,19 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
printer
,
err
=
NewJSONPathPrinter
(
string
(
data
))
printer
,
err
=
NewJSONPathPrinter
(
string
(
data
))
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"error parsing template %s, %v
\n
"
,
string
(
data
),
err
)
return
nil
,
false
,
fmt
.
Errorf
(
"error parsing template %s, %v
\n
"
,
string
(
data
),
err
)
}
case
"custom-columns"
:
var
err
error
if
printer
,
err
=
NewCustomColumnsPrinterFromSpec
(
formatArgument
);
err
!=
nil
{
return
nil
,
false
,
err
}
case
"custom-columns-file"
:
file
,
err
:=
os
.
Open
(
formatArgument
)
if
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"error reading template %s, %v
\n
"
,
formatArgument
,
err
)
}
if
printer
,
err
=
NewCustomColumnsPrinterFromTemplate
(
file
);
err
!=
nil
{
return
nil
,
false
,
err
}
}
case
"wide"
:
case
"wide"
:
fallthrough
fallthrough
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment