github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/service/formatter_test.go (about)

     1  package service
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/docker/cli/cli/command/formatter"
    11  	"github.com/docker/docker/api/types/swarm"
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  	"gotest.tools/v3/golden"
    15  )
    16  
    17  func TestServiceContextWrite(t *testing.T) {
    18  	var (
    19  		// we need a pair of variables for setting the job parameters, because
    20  		// those parameters take pointers to uint64, which we can't make as a
    21  		// literal
    22  		varThree uint64 = 3
    23  		varTen   uint64 = 10
    24  	)
    25  	cases := []struct {
    26  		context  formatter.Context
    27  		expected string
    28  	}{
    29  		// Errors
    30  		{
    31  			formatter.Context{Format: "{{InvalidFunction}}"},
    32  			`Template parsing error: template: :1: function "InvalidFunction" not defined
    33  `,
    34  		},
    35  		{
    36  			formatter.Context{Format: "{{nil}}"},
    37  			`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
    38  `,
    39  		},
    40  		// Table format
    41  		{
    42  			formatter.Context{Format: NewListFormat("table", false)},
    43  			`ID         NAME      MODE             REPLICAS               IMAGE     PORTS
    44  02_bar     bar       replicated       2/4                              *:80->8090/udp
    45  01_baz     baz       global           1/3                              *:80->8080/tcp
    46  04_qux2    qux2      replicated       3/3 (max 2 per node)             
    47  03_qux10   qux10     replicated       2/3 (max 1 per node)             
    48  05_job1    zarp1     replicated job   2/3 (5/10 completed)             
    49  06_job2    zarp2     global job       1/1 (3/4 completed)              
    50  `,
    51  		},
    52  		{
    53  			formatter.Context{Format: NewListFormat("table", true)},
    54  			`02_bar
    55  01_baz
    56  04_qux2
    57  03_qux10
    58  05_job1
    59  06_job2
    60  `,
    61  		},
    62  		{
    63  			formatter.Context{Format: NewListFormat("table {{.Name}}\t{{.Mode}}", false)},
    64  			`NAME      MODE
    65  bar       replicated
    66  baz       global
    67  qux2      replicated
    68  qux10     replicated
    69  zarp1     replicated job
    70  zarp2     global job
    71  `,
    72  		},
    73  		{
    74  			formatter.Context{Format: NewListFormat("table {{.Name}}", true)},
    75  			`NAME
    76  bar
    77  baz
    78  qux2
    79  qux10
    80  zarp1
    81  zarp2
    82  `,
    83  		},
    84  		// Raw Format
    85  		{
    86  			formatter.Context{Format: NewListFormat("raw", false)},
    87  			string(golden.Get(t, "service-context-write-raw.golden")),
    88  		},
    89  		{
    90  			formatter.Context{Format: NewListFormat("raw", true)},
    91  			`id: 02_bar
    92  id: 01_baz
    93  id: 04_qux2
    94  id: 03_qux10
    95  id: 05_job1
    96  id: 06_job2
    97  `,
    98  		},
    99  		// Custom Format
   100  		{
   101  			formatter.Context{Format: NewListFormat("{{.Name}}", false)},
   102  			`bar
   103  baz
   104  qux2
   105  qux10
   106  zarp1
   107  zarp2
   108  `,
   109  		},
   110  	}
   111  
   112  	services := []swarm.Service{
   113  		{
   114  			ID: "01_baz",
   115  			Spec: swarm.ServiceSpec{
   116  				Annotations: swarm.Annotations{Name: "baz"},
   117  				Mode: swarm.ServiceMode{
   118  					Global: &swarm.GlobalService{},
   119  				},
   120  			},
   121  			Endpoint: swarm.Endpoint{
   122  				Ports: []swarm.PortConfig{
   123  					{
   124  						PublishMode:   "ingress",
   125  						PublishedPort: 80,
   126  						TargetPort:    8080,
   127  						Protocol:      "tcp",
   128  					},
   129  				},
   130  			},
   131  			ServiceStatus: &swarm.ServiceStatus{
   132  				RunningTasks: 1,
   133  				DesiredTasks: 3,
   134  			},
   135  		},
   136  		{
   137  			ID: "02_bar",
   138  			Spec: swarm.ServiceSpec{
   139  				Annotations: swarm.Annotations{Name: "bar"},
   140  				Mode: swarm.ServiceMode{
   141  					Replicated: &swarm.ReplicatedService{},
   142  				},
   143  			},
   144  			Endpoint: swarm.Endpoint{
   145  				Ports: []swarm.PortConfig{
   146  					{
   147  						PublishMode:   "ingress",
   148  						PublishedPort: 80,
   149  						TargetPort:    8090,
   150  						Protocol:      "udp",
   151  					},
   152  				},
   153  			},
   154  			ServiceStatus: &swarm.ServiceStatus{
   155  				RunningTasks: 2,
   156  				DesiredTasks: 4,
   157  			},
   158  		},
   159  		{
   160  			ID: "03_qux10",
   161  			Spec: swarm.ServiceSpec{
   162  				Annotations: swarm.Annotations{Name: "qux10"},
   163  				Mode: swarm.ServiceMode{
   164  					Replicated: &swarm.ReplicatedService{},
   165  				},
   166  				TaskTemplate: swarm.TaskSpec{
   167  					Placement: &swarm.Placement{MaxReplicas: 1},
   168  				},
   169  			},
   170  			ServiceStatus: &swarm.ServiceStatus{
   171  				RunningTasks: 2,
   172  				DesiredTasks: 3,
   173  			},
   174  		},
   175  		{
   176  			ID: "04_qux2",
   177  			Spec: swarm.ServiceSpec{
   178  				Annotations: swarm.Annotations{Name: "qux2"},
   179  				Mode: swarm.ServiceMode{
   180  					Replicated: &swarm.ReplicatedService{},
   181  				},
   182  				TaskTemplate: swarm.TaskSpec{
   183  					Placement: &swarm.Placement{MaxReplicas: 2},
   184  				},
   185  			},
   186  			ServiceStatus: &swarm.ServiceStatus{
   187  				RunningTasks: 3,
   188  				DesiredTasks: 3,
   189  			},
   190  		},
   191  		{
   192  			ID: "05_job1",
   193  			Spec: swarm.ServiceSpec{
   194  				Annotations: swarm.Annotations{Name: "zarp1"},
   195  				Mode: swarm.ServiceMode{
   196  					ReplicatedJob: &swarm.ReplicatedJob{
   197  						MaxConcurrent:    &varThree,
   198  						TotalCompletions: &varTen,
   199  					},
   200  				},
   201  			},
   202  			ServiceStatus: &swarm.ServiceStatus{
   203  				RunningTasks:   2,
   204  				DesiredTasks:   3,
   205  				CompletedTasks: 5,
   206  			},
   207  		},
   208  		{
   209  			ID: "06_job2",
   210  			Spec: swarm.ServiceSpec{
   211  				Annotations: swarm.Annotations{Name: "zarp2"},
   212  				Mode: swarm.ServiceMode{
   213  					GlobalJob: &swarm.GlobalJob{},
   214  				},
   215  			},
   216  			ServiceStatus: &swarm.ServiceStatus{
   217  				RunningTasks:   1,
   218  				DesiredTasks:   1,
   219  				CompletedTasks: 3,
   220  			},
   221  		},
   222  	}
   223  
   224  	for _, tc := range cases {
   225  		tc := tc
   226  		t.Run(string(tc.context.Format), func(t *testing.T) {
   227  			var out bytes.Buffer
   228  			tc.context.Output = &out
   229  
   230  			if err := ListFormatWrite(tc.context, services); err != nil {
   231  				assert.Error(t, err, tc.expected)
   232  			} else {
   233  				assert.Equal(t, out.String(), tc.expected)
   234  			}
   235  		})
   236  	}
   237  }
   238  
   239  func TestServiceContextWriteJSON(t *testing.T) {
   240  	services := []swarm.Service{
   241  		{
   242  			ID: "01_baz",
   243  			Spec: swarm.ServiceSpec{
   244  				Annotations: swarm.Annotations{Name: "baz"},
   245  				Mode: swarm.ServiceMode{
   246  					Global: &swarm.GlobalService{},
   247  				},
   248  			},
   249  			Endpoint: swarm.Endpoint{
   250  				Ports: []swarm.PortConfig{
   251  					{
   252  						PublishMode:   "ingress",
   253  						PublishedPort: 80,
   254  						TargetPort:    8080,
   255  						Protocol:      "tcp",
   256  					},
   257  				},
   258  			},
   259  			ServiceStatus: &swarm.ServiceStatus{
   260  				RunningTasks: 1,
   261  				DesiredTasks: 3,
   262  			},
   263  		},
   264  		{
   265  			ID: "02_bar",
   266  			Spec: swarm.ServiceSpec{
   267  				Annotations: swarm.Annotations{Name: "bar"},
   268  				Mode: swarm.ServiceMode{
   269  					Replicated: &swarm.ReplicatedService{},
   270  				},
   271  			},
   272  			Endpoint: swarm.Endpoint{
   273  				Ports: []swarm.PortConfig{
   274  					{
   275  						PublishMode:   "ingress",
   276  						PublishedPort: 80,
   277  						TargetPort:    8080,
   278  						Protocol:      "tcp",
   279  					},
   280  				},
   281  			},
   282  			ServiceStatus: &swarm.ServiceStatus{
   283  				RunningTasks: 2,
   284  				DesiredTasks: 4,
   285  			},
   286  		},
   287  	}
   288  	expectedJSONs := []map[string]interface{}{
   289  		{"ID": "02_bar", "Name": "bar", "Mode": "replicated", "Replicas": "2/4", "Image": "", "Ports": "*:80->8080/tcp"},
   290  		{"ID": "01_baz", "Name": "baz", "Mode": "global", "Replicas": "1/3", "Image": "", "Ports": "*:80->8080/tcp"},
   291  	}
   292  
   293  	out := bytes.NewBufferString("")
   294  	err := ListFormatWrite(formatter.Context{Format: "{{json .}}", Output: out}, services)
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  	for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
   299  		msg := fmt.Sprintf("Output: line %d: %s", i, line)
   300  		var m map[string]interface{}
   301  		err := json.Unmarshal([]byte(line), &m)
   302  		assert.NilError(t, err, msg)
   303  		assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg)
   304  	}
   305  }
   306  func TestServiceContextWriteJSONField(t *testing.T) {
   307  	services := []swarm.Service{
   308  		{
   309  			ID: "01_baz",
   310  			Spec: swarm.ServiceSpec{
   311  				Annotations: swarm.Annotations{Name: "baz"},
   312  				Mode: swarm.ServiceMode{
   313  					Global: &swarm.GlobalService{},
   314  				},
   315  			},
   316  			ServiceStatus: &swarm.ServiceStatus{
   317  				RunningTasks: 2,
   318  				DesiredTasks: 4,
   319  			},
   320  		},
   321  		{
   322  			ID: "24_bar",
   323  			Spec: swarm.ServiceSpec{
   324  				Annotations: swarm.Annotations{Name: "bar"},
   325  				Mode: swarm.ServiceMode{
   326  					Replicated: &swarm.ReplicatedService{},
   327  				},
   328  			},
   329  			ServiceStatus: &swarm.ServiceStatus{
   330  				RunningTasks: 2,
   331  				DesiredTasks: 4,
   332  			},
   333  		},
   334  	}
   335  	out := bytes.NewBufferString("")
   336  	err := ListFormatWrite(formatter.Context{Format: "{{json .Name}}", Output: out}, services)
   337  	if err != nil {
   338  		t.Fatal(err)
   339  	}
   340  	for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
   341  		msg := fmt.Sprintf("Output: line %d: %s", i, line)
   342  		var s string
   343  		err := json.Unmarshal([]byte(line), &s)
   344  		assert.NilError(t, err, msg)
   345  		assert.Check(t, is.Equal(services[i].Spec.Name, s), msg)
   346  	}
   347  }
   348  
   349  func TestServiceContext_Ports(t *testing.T) {
   350  	c := serviceContext{
   351  		service: swarm.Service{
   352  			Endpoint: swarm.Endpoint{
   353  				Ports: []swarm.PortConfig{
   354  					{
   355  						Protocol:      "tcp",
   356  						TargetPort:    80,
   357  						PublishedPort: 81,
   358  						PublishMode:   "ingress",
   359  					},
   360  					{
   361  						Protocol:      "tcp",
   362  						TargetPort:    80,
   363  						PublishedPort: 80,
   364  						PublishMode:   "ingress",
   365  					},
   366  					{
   367  						Protocol:      "tcp",
   368  						TargetPort:    95,
   369  						PublishedPort: 95,
   370  						PublishMode:   "ingress",
   371  					},
   372  					{
   373  						Protocol:      "tcp",
   374  						TargetPort:    90,
   375  						PublishedPort: 90,
   376  						PublishMode:   "ingress",
   377  					},
   378  					{
   379  						Protocol:      "tcp",
   380  						TargetPort:    91,
   381  						PublishedPort: 91,
   382  						PublishMode:   "ingress",
   383  					},
   384  					{
   385  						Protocol:      "tcp",
   386  						TargetPort:    92,
   387  						PublishedPort: 92,
   388  						PublishMode:   "ingress",
   389  					},
   390  					{
   391  						Protocol:      "tcp",
   392  						TargetPort:    93,
   393  						PublishedPort: 93,
   394  						PublishMode:   "ingress",
   395  					},
   396  					{
   397  						Protocol:      "tcp",
   398  						TargetPort:    94,
   399  						PublishedPort: 94,
   400  						PublishMode:   "ingress",
   401  					},
   402  					{
   403  						Protocol:      "udp",
   404  						TargetPort:    95,
   405  						PublishedPort: 95,
   406  						PublishMode:   "ingress",
   407  					},
   408  					{
   409  						Protocol:      "udp",
   410  						TargetPort:    90,
   411  						PublishedPort: 90,
   412  						PublishMode:   "ingress",
   413  					},
   414  					{
   415  						Protocol:      "udp",
   416  						TargetPort:    96,
   417  						PublishedPort: 96,
   418  						PublishMode:   "ingress",
   419  					},
   420  					{
   421  						Protocol:      "udp",
   422  						TargetPort:    91,
   423  						PublishedPort: 91,
   424  						PublishMode:   "ingress",
   425  					},
   426  					{
   427  						Protocol:      "udp",
   428  						TargetPort:    92,
   429  						PublishedPort: 92,
   430  						PublishMode:   "ingress",
   431  					},
   432  					{
   433  						Protocol:      "udp",
   434  						TargetPort:    93,
   435  						PublishedPort: 93,
   436  						PublishMode:   "ingress",
   437  					},
   438  					{
   439  						Protocol:      "udp",
   440  						TargetPort:    94,
   441  						PublishedPort: 94,
   442  						PublishMode:   "ingress",
   443  					},
   444  					{
   445  						Protocol:      "tcp",
   446  						TargetPort:    60,
   447  						PublishedPort: 60,
   448  						PublishMode:   "ingress",
   449  					},
   450  					{
   451  						Protocol:      "tcp",
   452  						TargetPort:    61,
   453  						PublishedPort: 61,
   454  						PublishMode:   "ingress",
   455  					},
   456  					{
   457  						Protocol:      "tcp",
   458  						TargetPort:    61,
   459  						PublishedPort: 62,
   460  						PublishMode:   "ingress",
   461  					},
   462  					{
   463  						Protocol:      "sctp",
   464  						TargetPort:    97,
   465  						PublishedPort: 97,
   466  						PublishMode:   "ingress",
   467  					},
   468  					{
   469  						Protocol:      "sctp",
   470  						TargetPort:    98,
   471  						PublishedPort: 98,
   472  						PublishMode:   "ingress",
   473  					},
   474  				},
   475  			},
   476  		},
   477  	}
   478  
   479  	assert.Check(t, is.Equal("*:97-98->97-98/sctp, *:60-61->60-61/tcp, *:62->61/tcp, *:80-81->80/tcp, *:90-95->90-95/tcp, *:90-96->90-96/udp", c.Ports()))
   480  }