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 }