2016-09-08 13:11:39 -04:00
package formatter
import (
"bytes"
2016-09-13 03:01:31 -04:00
"encoding/json"
2016-09-08 13:11:39 -04:00
"fmt"
"strings"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/stringid"
2016-09-08 15:11:38 -04:00
"github.com/docker/docker/pkg/testutil/assert"
2016-09-08 13:11:39 -04:00
)
func TestContainerPsContext ( t * testing . T ) {
containerID := stringid . GenerateRandomID ( )
unix := time . Now ( ) . Add ( - 65 * time . Second ) . Unix ( )
var ctx containerContext
cases := [ ] struct {
container types . Container
trunc bool
expValue string
call func ( ) string
} {
2017-02-03 19:48:46 -05:00
{ types . Container { ID : containerID } , true , stringid . TruncateID ( containerID ) , ctx . ID } ,
{ types . Container { ID : containerID } , false , containerID , ctx . ID } ,
{ types . Container { Names : [ ] string { "/foobar_baz" } } , true , "foobar_baz" , ctx . Names } ,
{ types . Container { Image : "ubuntu" } , true , "ubuntu" , ctx . Image } ,
{ types . Container { Image : "verylongimagename" } , true , "verylongimagename" , ctx . Image } ,
{ types . Container { Image : "verylongimagename" } , false , "verylongimagename" , ctx . Image } ,
2016-09-08 13:11:39 -04:00
{ types . Container {
Image : "a5a665ff33eced1e0803148700880edab4" ,
ImageID : "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5" ,
} ,
true ,
"a5a665ff33ec" ,
ctx . Image ,
} ,
{ types . Container {
Image : "a5a665ff33eced1e0803148700880edab4" ,
ImageID : "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5" ,
} ,
false ,
"a5a665ff33eced1e0803148700880edab4" ,
ctx . Image ,
} ,
2017-02-03 19:48:46 -05:00
{ types . Container { Image : "" } , true , "<no image>" , ctx . Image } ,
{ types . Container { Command : "sh -c 'ls -la'" } , true , ` "sh -c 'ls -la'" ` , ctx . Command } ,
{ types . Container { Created : unix } , true , time . Unix ( unix , 0 ) . String ( ) , ctx . CreatedAt } ,
{ types . Container { Ports : [ ] types . Port { { PrivatePort : 8080 , PublicPort : 8080 , Type : "tcp" } } } , true , "8080/tcp" , ctx . Ports } ,
{ types . Container { Status : "RUNNING" } , true , "RUNNING" , ctx . Status } ,
{ types . Container { SizeRw : 10 } , true , "10B" , ctx . Size } ,
{ types . Container { SizeRw : 10 , SizeRootFs : 20 } , true , "10B (virtual 20B)" , ctx . Size } ,
{ types . Container { } , true , "" , ctx . Labels } ,
{ types . Container { Labels : map [ string ] string { "cpu" : "6" , "storage" : "ssd" } } , true , "cpu=6,storage=ssd" , ctx . Labels } ,
{ types . Container { Created : unix } , true , "About a minute ago" , ctx . RunningFor } ,
2016-09-08 13:11:39 -04:00
{ types . Container {
Mounts : [ ] types . MountPoint {
{
Name : "this-is-a-long-volume-name-and-will-be-truncated-if-trunc-is-set" ,
Driver : "local" ,
Source : "/a/path" ,
} ,
} ,
2017-02-03 19:48:46 -05:00
} , true , "this-is-a-lo..." , ctx . Mounts } ,
2016-09-08 13:11:39 -04:00
{ types . Container {
Mounts : [ ] types . MountPoint {
{
Driver : "local" ,
Source : "/a/path" ,
} ,
} ,
2017-02-03 19:48:46 -05:00
} , false , "/a/path" , ctx . Mounts } ,
2016-09-08 13:11:39 -04:00
{ types . Container {
Mounts : [ ] types . MountPoint {
{
Name : "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203" ,
Driver : "local" ,
Source : "/a/path" ,
} ,
} ,
2017-02-03 19:48:46 -05:00
} , false , "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203" , ctx . Mounts } ,
2016-09-08 13:11:39 -04:00
}
for _ , c := range cases {
ctx = containerContext { c : c . container , trunc : c . trunc }
v := c . call ( )
if strings . Contains ( v , "," ) {
compareMultipleValues ( t , v , c . expValue )
} else if v != c . expValue {
t . Fatalf ( "Expected %s, was %s\n" , c . expValue , v )
}
}
c1 := types . Container { Labels : map [ string ] string { "com.docker.swarm.swarm-id" : "33" , "com.docker.swarm.node_name" : "ubuntu" } }
ctx = containerContext { c : c1 , trunc : true }
sid := ctx . Label ( "com.docker.swarm.swarm-id" )
node := ctx . Label ( "com.docker.swarm.node_name" )
if sid != "33" {
t . Fatalf ( "Expected 33, was %s\n" , sid )
}
if node != "ubuntu" {
t . Fatalf ( "Expected ubuntu, was %s\n" , node )
}
c2 := types . Container { }
ctx = containerContext { c : c2 , trunc : true }
label := ctx . Label ( "anything.really" )
if label != "" {
t . Fatalf ( "Expected an empty string, was %s" , label )
}
}
func TestContainerContextWrite ( t * testing . T ) {
unixTime := time . Now ( ) . AddDate ( 0 , 0 , - 1 ) . Unix ( )
expectedTime := time . Unix ( unixTime , 0 ) . String ( )
2016-09-12 16:59:18 -04:00
cases := [ ] struct {
context Context
2016-09-08 13:11:39 -04:00
expected string
} {
// Errors
{
2016-09-12 16:59:18 -04:00
Context { Format : "{{InvalidFunction}}" } ,
2016-09-08 13:11:39 -04:00
` Template parsing error : template : : 1 : function "InvalidFunction" not defined
` ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : "{{nil}}" } ,
2016-09-08 13:11:39 -04:00
` Template parsing error : template : : 1 : 2 : executing "" at < nil > : nil is not a command
` ,
} ,
// Table Format
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "table" , false , true ) } ,
2016-09-08 13:11:39 -04:00
` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
2017-02-07 15:58:56 -05:00
containerID1 ubuntu "" 24 hours ago foobar_baz 0 B
containerID2 ubuntu "" 24 hours ago foobar_bar 0 B
2016-09-08 13:11:39 -04:00
` ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "table" , false , false ) } ,
2016-09-08 13:11:39 -04:00
` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
containerID1 ubuntu "" 24 hours ago foobar_baz
containerID2 ubuntu "" 24 hours ago foobar_bar
` ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "table {{.Image}}" , false , false ) } ,
2016-09-08 13:11:39 -04:00
"IMAGE\nubuntu\nubuntu\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "table {{.Image}}" , false , true ) } ,
2016-09-08 13:11:39 -04:00
"IMAGE\nubuntu\nubuntu\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "table {{.Image}}" , true , false ) } ,
2016-09-08 13:11:39 -04:00
"IMAGE\nubuntu\nubuntu\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "table" , true , false ) } ,
2016-09-08 13:11:39 -04:00
"containerID1\ncontainerID2\n" ,
} ,
// Raw Format
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "raw" , false , false ) } ,
2016-09-08 13:11:39 -04:00
fmt . Sprintf ( ` container_id : containerID1
image : ubuntu
command : ""
created_at : % s
2016-09-12 16:59:18 -04:00
status :
2016-09-08 13:11:39 -04:00
names : foobar_baz
2016-09-12 16:59:18 -04:00
labels :
ports :
2016-09-08 13:11:39 -04:00
container_id : containerID2
image : ubuntu
command : ""
created_at : % s
2016-09-12 16:59:18 -04:00
status :
2016-09-08 13:11:39 -04:00
names : foobar_bar
2016-09-12 16:59:18 -04:00
labels :
ports :
2016-09-08 13:11:39 -04:00
` , expectedTime , expectedTime ) ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "raw" , false , true ) } ,
2016-09-08 13:11:39 -04:00
fmt . Sprintf ( ` container_id : containerID1
image : ubuntu
command : ""
created_at : % s
2016-09-12 16:59:18 -04:00
status :
2016-09-08 13:11:39 -04:00
names : foobar_baz
2016-09-12 16:59:18 -04:00
labels :
ports :
2017-02-07 15:58:56 -05:00
size : 0 B
2016-09-08 13:11:39 -04:00
container_id : containerID2
image : ubuntu
command : ""
created_at : % s
2016-09-12 16:59:18 -04:00
status :
2016-09-08 13:11:39 -04:00
names : foobar_bar
2016-09-12 16:59:18 -04:00
labels :
ports :
2017-02-07 15:58:56 -05:00
size : 0 B
2016-09-08 13:11:39 -04:00
` , expectedTime , expectedTime ) ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "raw" , true , false ) } ,
2016-09-08 13:11:39 -04:00
"container_id: containerID1\ncontainer_id: containerID2\n" ,
} ,
// Custom Format
{
2016-09-12 16:59:18 -04:00
Context { Format : "{{.Image}}" } ,
2016-09-08 13:11:39 -04:00
"ubuntu\nubuntu\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context { Format : NewContainerFormat ( "{{.Image}}" , false , true ) } ,
2016-09-08 13:11:39 -04:00
"ubuntu\nubuntu\n" ,
} ,
2017-02-03 23:23:00 -05:00
// Special headers for customerized table format
{
Context { Format : NewContainerFormat ( ` table {{ truncate .ID 5 }} \t {{ json .Image }} {{ .RunningFor }} / {{ title .Status }} / {{ pad .Ports 2 2 }} . {{ upper .Names }} {{ lower .Status }} ` , false , true ) } ,
` CONTAINER ID IMAGE CREATED / STATUS / PORTS . NAMES STATUS
conta "ubuntu" 24 hours ago //.FOOBAR_BAZ
conta "ubuntu" 24 hours ago //.FOOBAR_BAR
` ,
} ,
2016-09-08 13:11:39 -04:00
}
2016-09-12 16:59:18 -04:00
for _ , testcase := range cases {
2016-09-08 13:11:39 -04:00
containers := [ ] types . Container {
{ ID : "containerID1" , Names : [ ] string { "/foobar_baz" } , Image : "ubuntu" , Created : unixTime } ,
{ ID : "containerID2" , Names : [ ] string { "/foobar_bar" } , Image : "ubuntu" , Created : unixTime } ,
}
out := bytes . NewBufferString ( "" )
2016-09-12 16:59:18 -04:00
testcase . context . Output = out
err := ContainerWrite ( testcase . context , containers )
if err != nil {
assert . Error ( t , err , testcase . expected )
} else {
assert . Equal ( t , out . String ( ) , testcase . expected )
}
2016-09-08 13:11:39 -04:00
}
}
func TestContainerContextWriteWithNoContainers ( t * testing . T ) {
out := bytes . NewBufferString ( "" )
containers := [ ] types . Container { }
contexts := [ ] struct {
2016-09-12 16:59:18 -04:00
context Context
2016-09-08 13:11:39 -04:00
expected string
} {
{
2016-09-12 16:59:18 -04:00
Context {
Format : "{{.Image}}" ,
Output : out ,
2016-09-08 13:11:39 -04:00
} ,
"" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context {
Format : "table {{.Image}}" ,
Output : out ,
2016-09-08 13:11:39 -04:00
} ,
"IMAGE\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context {
Format : NewContainerFormat ( "{{.Image}}" , false , true ) ,
Output : out ,
2016-09-08 13:11:39 -04:00
} ,
"" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context {
Format : NewContainerFormat ( "table {{.Image}}" , false , true ) ,
Output : out ,
2016-09-08 13:11:39 -04:00
} ,
"IMAGE\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context {
Format : "table {{.Image}}\t{{.Size}}" ,
Output : out ,
2016-09-08 13:11:39 -04:00
} ,
"IMAGE SIZE\n" ,
} ,
{
2016-09-12 16:59:18 -04:00
Context {
Format : NewContainerFormat ( "table {{.Image}}\t{{.Size}}" , false , true ) ,
Output : out ,
2016-09-08 13:11:39 -04:00
} ,
"IMAGE SIZE\n" ,
} ,
}
for _ , context := range contexts {
2016-09-12 16:59:18 -04:00
ContainerWrite ( context . context , containers )
assert . Equal ( t , context . expected , out . String ( ) )
2016-09-08 13:11:39 -04:00
// Clean buffer
out . Reset ( )
}
}
2016-09-13 03:01:31 -04:00
func TestContainerContextWriteJSON ( t * testing . T ) {
unix := time . Now ( ) . Add ( - 65 * time . Second ) . Unix ( )
containers := [ ] types . Container {
{ ID : "containerID1" , Names : [ ] string { "/foobar_baz" } , Image : "ubuntu" , Created : unix } ,
{ ID : "containerID2" , Names : [ ] string { "/foobar_bar" } , Image : "ubuntu" , Created : unix } ,
}
expectedCreated := time . Unix ( unix , 0 ) . String ( )
expectedJSONs := [ ] map [ string ] interface { } {
2017-02-03 19:48:46 -05:00
{ "Command" : "\"\"" , "CreatedAt" : expectedCreated , "ID" : "containerID1" , "Image" : "ubuntu" , "Labels" : "" , "LocalVolumes" : "0" , "Mounts" : "" , "Names" : "foobar_baz" , "Networks" : "" , "Ports" : "" , "RunningFor" : "About a minute ago" , "Size" : "0B" , "Status" : "" } ,
{ "Command" : "\"\"" , "CreatedAt" : expectedCreated , "ID" : "containerID2" , "Image" : "ubuntu" , "Labels" : "" , "LocalVolumes" : "0" , "Mounts" : "" , "Names" : "foobar_bar" , "Networks" : "" , "Ports" : "" , "RunningFor" : "About a minute ago" , "Size" : "0B" , "Status" : "" } ,
2016-09-13 03:01:31 -04:00
}
out := bytes . NewBufferString ( "" )
err := ContainerWrite ( Context { Format : "{{json .}}" , Output : out } , containers )
if err != nil {
t . Fatal ( err )
}
for i , line := range strings . Split ( strings . TrimSpace ( out . String ( ) ) , "\n" ) {
t . Logf ( "Output: line %d: %s" , i , line )
var m map [ string ] interface { }
if err := json . Unmarshal ( [ ] byte ( line ) , & m ) ; err != nil {
t . Fatal ( err )
}
assert . DeepEqual ( t , m , expectedJSONs [ i ] )
}
}
func TestContainerContextWriteJSONField ( t * testing . T ) {
containers := [ ] types . Container {
{ ID : "containerID1" , Names : [ ] string { "/foobar_baz" } , Image : "ubuntu" } ,
{ ID : "containerID2" , Names : [ ] string { "/foobar_bar" } , Image : "ubuntu" } ,
}
out := bytes . NewBufferString ( "" )
err := ContainerWrite ( Context { Format : "{{json .ID}}" , Output : out } , containers )
if err != nil {
t . Fatal ( err )
}
for i , line := range strings . Split ( strings . TrimSpace ( out . String ( ) ) , "\n" ) {
t . Logf ( "Output: line %d: %s" , i , line )
var s string
if err := json . Unmarshal ( [ ] byte ( line ) , & s ) ; err != nil {
t . Fatal ( err )
}
assert . Equal ( t , s , containers [ i ] . ID )
}
}
2016-11-12 11:10:27 -05:00
func TestContainerBackCompat ( t * testing . T ) {
2016-11-17 01:17:40 -05:00
containers := [ ] types . Container { { ID : "brewhaha" } }
2016-11-12 11:10:27 -05:00
cases := [ ] string {
"ID" ,
"Names" ,
"Image" ,
"Command" ,
"CreatedAt" ,
"RunningFor" ,
"Ports" ,
"Status" ,
"Size" ,
"Labels" ,
"Mounts" ,
}
buf := bytes . NewBuffer ( nil )
for _ , c := range cases {
ctx := Context { Format : Format ( fmt . Sprintf ( "{{ .%s }}" , c ) ) , Output : buf }
if err := ContainerWrite ( ctx , containers ) ; err != nil {
2016-11-17 01:17:40 -05:00
t . Logf ( "could not render template for field '%s': %v" , c , err )
2016-11-12 11:10:27 -05:00
t . Fail ( )
}
buf . Reset ( )
}
}