github.com/sld880311/docker@v0.0.0-20200524143708-d5593973a475/cli/command/formatter/container_test.go (about)

     1  package formatter
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/pkg/stringid"
    13  	"github.com/docker/docker/pkg/testutil/assert"
    14  )
    15  
    16  func TestContainerPsContext(t *testing.T) {
    17  	containerID := stringid.GenerateRandomID()
    18  	unix := time.Now().Add(-65 * time.Second).Unix()
    19  
    20  	var ctx containerContext
    21  	cases := []struct {
    22  		container types.Container
    23  		trunc     bool
    24  		expValue  string
    25  		expHeader string
    26  		call      func() string
    27  	}{
    28  		{types.Container{ID: containerID}, true, stringid.TruncateID(containerID), containerIDHeader, ctx.ID},
    29  		{types.Container{ID: containerID}, false, containerID, containerIDHeader, ctx.ID},
    30  		{types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
    31  		{types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
    32  		{types.Container{Image: "verylongimagename"}, true, "verylongimagename", imageHeader, ctx.Image},
    33  		{types.Container{Image: "verylongimagename"}, false, "verylongimagename", imageHeader, ctx.Image},
    34  		{types.Container{
    35  			Image:   "a5a665ff33eced1e0803148700880edab4",
    36  			ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5",
    37  		},
    38  			true,
    39  			"a5a665ff33ec",
    40  			imageHeader,
    41  			ctx.Image,
    42  		},
    43  		{types.Container{
    44  			Image:   "a5a665ff33eced1e0803148700880edab4",
    45  			ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5",
    46  		},
    47  			false,
    48  			"a5a665ff33eced1e0803148700880edab4",
    49  			imageHeader,
    50  			ctx.Image,
    51  		},
    52  		{types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
    53  		{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
    54  		{types.Container{Created: unix}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
    55  		{types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
    56  		{types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
    57  		{types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size},
    58  		{types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10 B (virtual 20 B)", sizeHeader, ctx.Size},
    59  		{types.Container{}, true, "", labelsHeader, ctx.Labels},
    60  		{types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", labelsHeader, ctx.Labels},
    61  		{types.Container{Created: unix}, true, "About a minute", runningForHeader, ctx.RunningFor},
    62  		{types.Container{
    63  			Mounts: []types.MountPoint{
    64  				{
    65  					Name:   "this-is-a-long-volume-name-and-will-be-truncated-if-trunc-is-set",
    66  					Driver: "local",
    67  					Source: "/a/path",
    68  				},
    69  			},
    70  		}, true, "this-is-a-lo...", mountsHeader, ctx.Mounts},
    71  		{types.Container{
    72  			Mounts: []types.MountPoint{
    73  				{
    74  					Driver: "local",
    75  					Source: "/a/path",
    76  				},
    77  			},
    78  		}, false, "/a/path", mountsHeader, ctx.Mounts},
    79  		{types.Container{
    80  			Mounts: []types.MountPoint{
    81  				{
    82  					Name:   "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203",
    83  					Driver: "local",
    84  					Source: "/a/path",
    85  				},
    86  			},
    87  		}, false, "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", mountsHeader, ctx.Mounts},
    88  	}
    89  
    90  	for _, c := range cases {
    91  		ctx = containerContext{c: c.container, trunc: c.trunc}
    92  		v := c.call()
    93  		if strings.Contains(v, ",") {
    94  			compareMultipleValues(t, v, c.expValue)
    95  		} else if v != c.expValue {
    96  			t.Fatalf("Expected %s, was %s\n", c.expValue, v)
    97  		}
    98  
    99  		h := ctx.FullHeader()
   100  		if h != c.expHeader {
   101  			t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
   102  		}
   103  	}
   104  
   105  	c1 := types.Container{Labels: map[string]string{"com.docker.swarm.swarm-id": "33", "com.docker.swarm.node_name": "ubuntu"}}
   106  	ctx = containerContext{c: c1, trunc: true}
   107  
   108  	sid := ctx.Label("com.docker.swarm.swarm-id")
   109  	node := ctx.Label("com.docker.swarm.node_name")
   110  	if sid != "33" {
   111  		t.Fatalf("Expected 33, was %s\n", sid)
   112  	}
   113  
   114  	if node != "ubuntu" {
   115  		t.Fatalf("Expected ubuntu, was %s\n", node)
   116  	}
   117  
   118  	h := ctx.FullHeader()
   119  	if h != "SWARM ID\tNODE NAME" {
   120  		t.Fatalf("Expected %s, was %s\n", "SWARM ID\tNODE NAME", h)
   121  
   122  	}
   123  
   124  	c2 := types.Container{}
   125  	ctx = containerContext{c: c2, trunc: true}
   126  
   127  	label := ctx.Label("anything.really")
   128  	if label != "" {
   129  		t.Fatalf("Expected an empty string, was %s", label)
   130  	}
   131  
   132  	ctx = containerContext{c: c2, trunc: true}
   133  	FullHeader := ctx.FullHeader()
   134  	if FullHeader != "" {
   135  		t.Fatalf("Expected FullHeader to be empty, was %s", FullHeader)
   136  	}
   137  
   138  }
   139  
   140  func TestContainerContextWrite(t *testing.T) {
   141  	unixTime := time.Now().AddDate(0, 0, -1).Unix()
   142  	expectedTime := time.Unix(unixTime, 0).String()
   143  
   144  	cases := []struct {
   145  		context  Context
   146  		expected string
   147  	}{
   148  		// Errors
   149  		{
   150  			Context{Format: "{{InvalidFunction}}"},
   151  			`Template parsing error: template: :1: function "InvalidFunction" not defined
   152  `,
   153  		},
   154  		{
   155  			Context{Format: "{{nil}}"},
   156  			`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
   157  `,
   158  		},
   159  		// Table Format
   160  		{
   161  			Context{Format: NewContainerFormat("table", false, true)},
   162  			`CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES               SIZE
   163  containerID1        ubuntu              ""                  24 hours ago                                                foobar_baz          0 B
   164  containerID2        ubuntu              ""                  24 hours ago                                                foobar_bar          0 B
   165  `,
   166  		},
   167  		{
   168  			Context{Format: NewContainerFormat("table", false, false)},
   169  			`CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
   170  containerID1        ubuntu              ""                  24 hours ago                                                foobar_baz
   171  containerID2        ubuntu              ""                  24 hours ago                                                foobar_bar
   172  `,
   173  		},
   174  		{
   175  			Context{Format: NewContainerFormat("table {{.Image}}", false, false)},
   176  			"IMAGE\nubuntu\nubuntu\n",
   177  		},
   178  		{
   179  			Context{Format: NewContainerFormat("table {{.Image}}", false, true)},
   180  			"IMAGE\nubuntu\nubuntu\n",
   181  		},
   182  		{
   183  			Context{Format: NewContainerFormat("table {{.Image}}", true, false)},
   184  			"IMAGE\nubuntu\nubuntu\n",
   185  		},
   186  		{
   187  			Context{Format: NewContainerFormat("table", true, false)},
   188  			"containerID1\ncontainerID2\n",
   189  		},
   190  		// Raw Format
   191  		{
   192  			Context{Format: NewContainerFormat("raw", false, false)},
   193  			fmt.Sprintf(`container_id: containerID1
   194  image: ubuntu
   195  command: ""
   196  created_at: %s
   197  status:
   198  names: foobar_baz
   199  labels:
   200  ports:
   201  
   202  container_id: containerID2
   203  image: ubuntu
   204  command: ""
   205  created_at: %s
   206  status:
   207  names: foobar_bar
   208  labels:
   209  ports:
   210  
   211  `, expectedTime, expectedTime),
   212  		},
   213  		{
   214  			Context{Format: NewContainerFormat("raw", false, true)},
   215  			fmt.Sprintf(`container_id: containerID1
   216  image: ubuntu
   217  command: ""
   218  created_at: %s
   219  status:
   220  names: foobar_baz
   221  labels:
   222  ports:
   223  size: 0 B
   224  
   225  container_id: containerID2
   226  image: ubuntu
   227  command: ""
   228  created_at: %s
   229  status:
   230  names: foobar_bar
   231  labels:
   232  ports:
   233  size: 0 B
   234  
   235  `, expectedTime, expectedTime),
   236  		},
   237  		{
   238  			Context{Format: NewContainerFormat("raw", true, false)},
   239  			"container_id: containerID1\ncontainer_id: containerID2\n",
   240  		},
   241  		// Custom Format
   242  		{
   243  			Context{Format: "{{.Image}}"},
   244  			"ubuntu\nubuntu\n",
   245  		},
   246  		{
   247  			Context{Format: NewContainerFormat("{{.Image}}", false, true)},
   248  			"ubuntu\nubuntu\n",
   249  		},
   250  	}
   251  
   252  	for _, testcase := range cases {
   253  		containers := []types.Container{
   254  			{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unixTime},
   255  			{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unixTime},
   256  		}
   257  		out := bytes.NewBufferString("")
   258  		testcase.context.Output = out
   259  		err := ContainerWrite(testcase.context, containers)
   260  		if err != nil {
   261  			assert.Error(t, err, testcase.expected)
   262  		} else {
   263  			assert.Equal(t, out.String(), testcase.expected)
   264  		}
   265  	}
   266  }
   267  
   268  func TestContainerContextWriteWithNoContainers(t *testing.T) {
   269  	out := bytes.NewBufferString("")
   270  	containers := []types.Container{}
   271  
   272  	contexts := []struct {
   273  		context  Context
   274  		expected string
   275  	}{
   276  		{
   277  			Context{
   278  				Format: "{{.Image}}",
   279  				Output: out,
   280  			},
   281  			"",
   282  		},
   283  		{
   284  			Context{
   285  				Format: "table {{.Image}}",
   286  				Output: out,
   287  			},
   288  			"IMAGE\n",
   289  		},
   290  		{
   291  			Context{
   292  				Format: NewContainerFormat("{{.Image}}", false, true),
   293  				Output: out,
   294  			},
   295  			"",
   296  		},
   297  		{
   298  			Context{
   299  				Format: NewContainerFormat("table {{.Image}}", false, true),
   300  				Output: out,
   301  			},
   302  			"IMAGE\n",
   303  		},
   304  		{
   305  			Context{
   306  				Format: "table {{.Image}}\t{{.Size}}",
   307  				Output: out,
   308  			},
   309  			"IMAGE               SIZE\n",
   310  		},
   311  		{
   312  			Context{
   313  				Format: NewContainerFormat("table {{.Image}}\t{{.Size}}", false, true),
   314  				Output: out,
   315  			},
   316  			"IMAGE               SIZE\n",
   317  		},
   318  	}
   319  
   320  	for _, context := range contexts {
   321  		ContainerWrite(context.context, containers)
   322  		assert.Equal(t, context.expected, out.String())
   323  		// Clean buffer
   324  		out.Reset()
   325  	}
   326  }
   327  
   328  func TestContainerContextWriteJSON(t *testing.T) {
   329  	unix := time.Now().Add(-65 * time.Second).Unix()
   330  	containers := []types.Container{
   331  		{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unix},
   332  		{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unix},
   333  	}
   334  	expectedCreated := time.Unix(unix, 0).String()
   335  	expectedJSONs := []map[string]interface{}{
   336  		{"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID1", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_baz", "Networks": "", "Ports": "", "RunningFor": "About a minute", "Size": "0 B", "Status": ""},
   337  		{"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID2", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_bar", "Networks": "", "Ports": "", "RunningFor": "About a minute", "Size": "0 B", "Status": ""},
   338  	}
   339  	out := bytes.NewBufferString("")
   340  	err := ContainerWrite(Context{Format: "{{json .}}", Output: out}, containers)
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  	for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
   345  		t.Logf("Output: line %d: %s", i, line)
   346  		var m map[string]interface{}
   347  		if err := json.Unmarshal([]byte(line), &m); err != nil {
   348  			t.Fatal(err)
   349  		}
   350  		assert.DeepEqual(t, m, expectedJSONs[i])
   351  	}
   352  }
   353  
   354  func TestContainerContextWriteJSONField(t *testing.T) {
   355  	containers := []types.Container{
   356  		{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu"},
   357  		{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu"},
   358  	}
   359  	out := bytes.NewBufferString("")
   360  	err := ContainerWrite(Context{Format: "{{json .ID}}", Output: out}, containers)
   361  	if err != nil {
   362  		t.Fatal(err)
   363  	}
   364  	for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
   365  		t.Logf("Output: line %d: %s", i, line)
   366  		var s string
   367  		if err := json.Unmarshal([]byte(line), &s); err != nil {
   368  			t.Fatal(err)
   369  		}
   370  		assert.Equal(t, s, containers[i].ID)
   371  	}
   372  }
   373  
   374  func TestContainerBackCompat(t *testing.T) {
   375  	containers := []types.Container{{ID: "brewhaha"}}
   376  	cases := []string{
   377  		"ID",
   378  		"Names",
   379  		"Image",
   380  		"Command",
   381  		"CreatedAt",
   382  		"RunningFor",
   383  		"Ports",
   384  		"Status",
   385  		"Size",
   386  		"Labels",
   387  		"Mounts",
   388  	}
   389  	buf := bytes.NewBuffer(nil)
   390  	for _, c := range cases {
   391  		ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
   392  		if err := ContainerWrite(ctx, containers); err != nil {
   393  			t.Logf("could not render template for field '%s': %v", c, err)
   394  			t.Fail()
   395  		}
   396  		buf.Reset()
   397  	}
   398  }