github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/compose_ps_linux_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/containerd/nerdctl/pkg/tabutil"
    27  	"github.com/containerd/nerdctl/pkg/testutil"
    28  	"gotest.tools/v3/assert"
    29  )
    30  
    31  func TestComposePs(t *testing.T) {
    32  	base := testutil.NewBase(t)
    33  	var dockerComposeYAML = fmt.Sprintf(`
    34  version: '3.1'
    35  
    36  services:
    37    wordpress:
    38      image: %s
    39      container_name: wordpress_container
    40      ports:
    41        - 8080:80
    42      environment:
    43        WORDPRESS_DB_HOST: db
    44        WORDPRESS_DB_USER: exampleuser
    45        WORDPRESS_DB_PASSWORD: examplepass
    46        WORDPRESS_DB_NAME: exampledb
    47      volumes:
    48        - wordpress:/var/www/html
    49    db:
    50      image: %s
    51      container_name: db_container
    52      environment:
    53        MYSQL_DATABASE: exampledb
    54        MYSQL_USER: exampleuser
    55        MYSQL_PASSWORD: examplepass
    56        MYSQL_RANDOM_ROOT_PASSWORD: '1'
    57      volumes:
    58        - db:/var/lib/mysql
    59    alpine:
    60      image: %s
    61      container_name: alpine_container
    62  
    63  volumes:
    64    wordpress:
    65    db:
    66  `, testutil.WordpressImage, testutil.MariaDBImage, testutil.AlpineImage)
    67  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
    68  	defer comp.CleanUp()
    69  	projectName := comp.ProjectName()
    70  	t.Logf("projectName=%q", projectName)
    71  
    72  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
    73  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
    74  
    75  	assertHandler := func(expectedName, expectedImage string) func(stdout string) error {
    76  		return func(stdout string) error {
    77  			lines := strings.Split(strings.TrimSpace(stdout), "\n")
    78  			if len(lines) < 2 {
    79  				return fmt.Errorf("expected at least 2 lines, got %d", len(lines))
    80  			}
    81  
    82  			tab := tabutil.NewReader("NAME\tIMAGE\tCOMMAND\tSERVICE\tSTATUS\tPORTS")
    83  			err := tab.ParseHeader(lines[0])
    84  			if err != nil {
    85  				return fmt.Errorf("failed to parse header: %v", err)
    86  			}
    87  
    88  			container, _ := tab.ReadRow(lines[1], "NAME")
    89  			assert.Equal(t, container, expectedName)
    90  
    91  			image, _ := tab.ReadRow(lines[1], "IMAGE")
    92  			assert.Equal(t, image, expectedImage)
    93  
    94  			return nil
    95  		}
    96  
    97  	}
    98  
    99  	time.Sleep(3 * time.Second)
   100  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "wordpress").AssertOutWithFunc(assertHandler("wordpress_container", testutil.WordpressImage))
   101  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "db").AssertOutWithFunc(assertHandler("db_container", testutil.MariaDBImage))
   102  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps").AssertOutNotContains(testutil.AlpineImage)
   103  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "alpine", "-a").AssertOutWithFunc(assertHandler("alpine_container", testutil.AlpineImage))
   104  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "-a", "--filter", "status=exited").AssertOutWithFunc(assertHandler("alpine_container", testutil.AlpineImage))
   105  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--services", "-a").AssertOutContainsAll("wordpress\n", "db\n", "alpine\n")
   106  }
   107  
   108  func TestComposePsJSON(t *testing.T) {
   109  	// docker parses unknown 'format' as a Go template and won't output an error
   110  	testutil.DockerIncompatible(t)
   111  
   112  	base := testutil.NewBase(t)
   113  	var dockerComposeYAML = fmt.Sprintf(`
   114  version: '3.1'
   115  
   116  services:
   117    wordpress:
   118      image: %s
   119      ports:
   120        - 8080:80
   121      environment:
   122        WORDPRESS_DB_HOST: db
   123        WORDPRESS_DB_USER: exampleuser
   124        WORDPRESS_DB_PASSWORD: examplepass
   125        WORDPRESS_DB_NAME: exampledb
   126      volumes:
   127        - wordpress:/var/www/html
   128    db:
   129      image: %s
   130      environment:
   131        MYSQL_DATABASE: exampledb
   132        MYSQL_USER: exampleuser
   133        MYSQL_PASSWORD: examplepass
   134        MYSQL_RANDOM_ROOT_PASSWORD: '1'
   135      volumes:
   136        - db:/var/lib/mysql
   137  
   138  volumes:
   139    wordpress:
   140    db:
   141  `, testutil.WordpressImage, testutil.MariaDBImage)
   142  
   143  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   144  	defer comp.CleanUp()
   145  	projectName := comp.ProjectName()
   146  	t.Logf("projectName=%q", projectName)
   147  
   148  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   149  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   150  
   151  	assertHandler := func(svc string, count int, fields ...string) func(stdout string) error {
   152  		return func(stdout string) error {
   153  			// 1. check json output can be unmarshalled back to printables.
   154  			var printables []composeContainerPrintable
   155  			if err := json.Unmarshal([]byte(stdout), &printables); err != nil {
   156  				return fmt.Errorf("[service: %s]failed to unmarshal json output from `compose ps`: %s", svc, stdout)
   157  			}
   158  			// 2. check #printables matches expected count.
   159  			if len(printables) != count {
   160  				return fmt.Errorf("[service: %s]unmarshal generates %d printables, expected %d: %s", svc, len(printables), count, stdout)
   161  			}
   162  			// 3. check marshalled json string has all expected substrings.
   163  			for _, field := range fields {
   164  				if !strings.Contains(stdout, field) {
   165  					return fmt.Errorf("[service: %s]marshalled json output doesn't have expected string (%s): %s", svc, field, stdout)
   166  				}
   167  			}
   168  			return nil
   169  		}
   170  	}
   171  
   172  	// check other formats are not supported
   173  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--format", "yaml").AssertFail()
   174  	// check all services are up (can be marshalled and unmarshalled) and check Image field exists
   175  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--format", "json").
   176  		AssertOutWithFunc(assertHandler("all", 2, `"Service":"wordpress"`, `"Service":"db"`,
   177  			fmt.Sprintf(`"Image":"%s"`, testutil.WordpressImage), fmt.Sprintf(`"Image":"%s"`, testutil.MariaDBImage)))
   178  	// check wordpress is running
   179  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--format", "json", "wordpress").
   180  		AssertOutWithFunc(assertHandler("wordpress", 1, `"Service":"wordpress"`, `"State":"running"`, `"TargetPort":80`, `"PublishedPort":8080`))
   181  	// check wordpress is stopped
   182  	base.ComposeCmd("-f", comp.YAMLFullPath(), "stop", "wordpress").AssertOK()
   183  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--format", "json", "wordpress", "-a").
   184  		AssertOutWithFunc(assertHandler("wordpress", 1, `"Service":"wordpress"`, `"State":"exited"`))
   185  	// check wordpress is removed
   186  	base.ComposeCmd("-f", comp.YAMLFullPath(), "rm", "-f", "wordpress").AssertOK()
   187  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--format", "json", "wordpress").
   188  		AssertOutWithFunc(assertHandler("wordpress", 0))
   189  }