github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/compose_up_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  	"fmt"
    21  	"io"
    22  	"os"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/containerd/log"
    28  	"github.com/containerd/nerdctl/pkg/composer/serviceparser"
    29  	"github.com/containerd/nerdctl/pkg/rootlessutil"
    30  	"github.com/docker/go-connections/nat"
    31  
    32  	"github.com/containerd/nerdctl/pkg/testutil"
    33  	"github.com/containerd/nerdctl/pkg/testutil/nettestutil"
    34  
    35  	"gotest.tools/v3/assert"
    36  )
    37  
    38  func TestComposeUp(t *testing.T) {
    39  	base := testutil.NewBase(t)
    40  	testComposeUp(t, base, fmt.Sprintf(`
    41  version: '3.1'
    42  
    43  services:
    44  
    45    wordpress:
    46      image: %s
    47      restart: always
    48      ports:
    49        - 8080:80
    50      environment:
    51        WORDPRESS_DB_HOST: db
    52        WORDPRESS_DB_USER: exampleuser
    53        WORDPRESS_DB_PASSWORD: examplepass
    54        WORDPRESS_DB_NAME: exampledb
    55      volumes:
    56        - wordpress:/var/www/html
    57  
    58    db:
    59      image: %s
    60      restart: always
    61      environment:
    62        MYSQL_DATABASE: exampledb
    63        MYSQL_USER: exampleuser
    64        MYSQL_PASSWORD: examplepass
    65        MYSQL_RANDOM_ROOT_PASSWORD: '1'
    66      volumes:
    67        - db:/var/lib/mysql
    68  
    69  volumes:
    70    wordpress:
    71    db:
    72  `, testutil.WordpressImage, testutil.MariaDBImage))
    73  }
    74  
    75  func testComposeUp(t *testing.T, base *testutil.Base, dockerComposeYAML string, opts ...string) {
    76  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
    77  	defer comp.CleanUp()
    78  
    79  	projectName := comp.ProjectName()
    80  	t.Logf("projectName=%q", projectName)
    81  
    82  	base.ComposeCmd(append(append([]string{"-f", comp.YAMLFullPath()}, opts...), "up", "-d")...).AssertOK()
    83  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
    84  	base.Cmd("volume", "inspect", fmt.Sprintf("%s_db", projectName)).AssertOK()
    85  	base.Cmd("network", "inspect", fmt.Sprintf("%s_default", projectName)).AssertOK()
    86  
    87  	checkWordpress := func() error {
    88  		resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 10, false)
    89  		if err != nil {
    90  			return err
    91  		}
    92  		respBody, err := io.ReadAll(resp.Body)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		if !strings.Contains(string(respBody), testutil.WordpressIndexHTMLSnippet) {
    97  			t.Logf("respBody=%q", respBody)
    98  			return fmt.Errorf("respBody does not contain %q", testutil.WordpressIndexHTMLSnippet)
    99  		}
   100  		return nil
   101  	}
   102  
   103  	var wordpressWorking bool
   104  	for i := 0; i < 30; i++ {
   105  		t.Logf("(retry %d)", i)
   106  		err := checkWordpress()
   107  		if err == nil {
   108  			wordpressWorking = true
   109  			break
   110  		}
   111  		// NOTE: "<h1>Error establishing a database connection</h1>" is expected for the first few iterations
   112  		t.Log(err)
   113  		time.Sleep(3 * time.Second)
   114  	}
   115  
   116  	if !wordpressWorking {
   117  		t.Fatal("wordpress is not working")
   118  	}
   119  	t.Log("wordpress seems functional")
   120  
   121  	base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").AssertOK()
   122  	base.Cmd("volume", "inspect", fmt.Sprintf("%s_db", projectName)).AssertFail()
   123  	base.Cmd("network", "inspect", fmt.Sprintf("%s_default", projectName)).AssertFail()
   124  }
   125  
   126  func TestComposeUpBuild(t *testing.T) {
   127  	testutil.RequiresBuild(t)
   128  	base := testutil.NewBase(t)
   129  	defer base.Cmd("builder", "prune").Run()
   130  
   131  	const dockerComposeYAML = `
   132  services:
   133    web:
   134      build: .
   135      ports:
   136      - 8080:80
   137  `
   138  	dockerfile := fmt.Sprintf(`FROM %s
   139  COPY index.html /usr/share/nginx/html/index.html
   140  `, testutil.NginxAlpineImage)
   141  	indexHTML := t.Name()
   142  
   143  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   144  	defer comp.CleanUp()
   145  	projectName := comp.ProjectName()
   146  	t.Logf("projectName=%q", projectName)
   147  
   148  	comp.WriteFile("Dockerfile", dockerfile)
   149  	comp.WriteFile("index.html", indexHTML)
   150  
   151  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d", "--build").AssertOK()
   152  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   153  
   154  	resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 50, false)
   155  	assert.NilError(t, err)
   156  	respBody, err := io.ReadAll(resp.Body)
   157  	assert.NilError(t, err)
   158  	t.Logf("respBody=%q", respBody)
   159  	assert.Assert(t, strings.Contains(string(respBody), indexHTML))
   160  }
   161  
   162  func TestComposeUpNetWithStaticIP(t *testing.T) {
   163  	if rootlessutil.IsRootless() {
   164  		t.Skip("Static IP assignment is not supported rootless mode yet.")
   165  	}
   166  	base := testutil.NewBase(t)
   167  	staticIP := "172.20.0.12"
   168  	var dockerComposeYAML = fmt.Sprintf(`
   169  version: '3.1'
   170  
   171  services:
   172    svc0:
   173      image: %s
   174      networks:
   175        net0:
   176          ipv4_address: %s
   177  
   178  networks:
   179    net0:
   180      ipam:
   181        config:
   182          - subnet: 172.20.0.0/24
   183  `, testutil.NginxAlpineImage, staticIP)
   184  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   185  	defer comp.CleanUp()
   186  	projectName := comp.ProjectName()
   187  	t.Logf("projectName=%q", projectName)
   188  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   189  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   190  
   191  	svc0 := serviceparser.DefaultContainerName(projectName, "svc0", "1")
   192  	inspectCmd := base.Cmd("inspect", svc0, "--format", "\"{{range .NetworkSettings.Networks}} {{.IPAddress}}{{end}}\"")
   193  	result := inspectCmd.Run()
   194  	stdoutContent := result.Stdout() + result.Stderr()
   195  	assert.Assert(inspectCmd.Base.T, result.ExitCode == 0, stdoutContent)
   196  	if !strings.Contains(stdoutContent, staticIP) {
   197  		log.L.Errorf("test failed, the actual container ip is %s", stdoutContent)
   198  		t.Fail()
   199  		return
   200  	}
   201  }
   202  
   203  func TestComposeUpMultiNet(t *testing.T) {
   204  	base := testutil.NewBase(t)
   205  
   206  	var dockerComposeYAML = fmt.Sprintf(`
   207  version: '3.1'
   208  
   209  services:
   210    svc0:
   211      image: %s
   212      networks:
   213        - net0
   214        - net1
   215        - net2
   216    svc1:
   217      image: %s
   218      networks:
   219        - net0
   220        - net1
   221    svc2:
   222      image: %s
   223      networks:
   224        - net2
   225  
   226  networks:
   227    net0: {}
   228    net1: {}
   229    net2: {}
   230  `, testutil.NginxAlpineImage, testutil.NginxAlpineImage, testutil.NginxAlpineImage)
   231  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   232  	defer comp.CleanUp()
   233  
   234  	projectName := comp.ProjectName()
   235  	t.Logf("projectName=%q", projectName)
   236  
   237  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   238  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   239  
   240  	svc0 := serviceparser.DefaultContainerName(projectName, "svc0", "1")
   241  	svc1 := serviceparser.DefaultContainerName(projectName, "svc1", "1")
   242  	svc2 := serviceparser.DefaultContainerName(projectName, "svc2", "1")
   243  
   244  	base.Cmd("exec", svc0, "ping", "-c", "1", "svc0").AssertOK()
   245  	base.Cmd("exec", svc0, "ping", "-c", "1", "svc1").AssertOK()
   246  	base.Cmd("exec", svc0, "ping", "-c", "1", "svc2").AssertOK()
   247  	base.Cmd("exec", svc1, "ping", "-c", "1", "svc0").AssertOK()
   248  	base.Cmd("exec", svc2, "ping", "-c", "1", "svc0").AssertOK()
   249  	base.Cmd("exec", svc1, "ping", "-c", "1", "svc2").AssertFail()
   250  }
   251  
   252  func TestComposeUpOsEnvVar(t *testing.T) {
   253  	base := testutil.NewBase(t)
   254  	const containerName = "nginxAlpine"
   255  	var dockerComposeYAML = fmt.Sprintf(`
   256  version: '3.1'
   257  
   258  services:
   259    svc1:
   260      image: %s
   261      container_name: %s
   262      ports:
   263        - ${ADDRESS:-127.0.0.1}:8080:80
   264  `, testutil.NginxAlpineImage, containerName)
   265  
   266  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   267  	defer comp.CleanUp()
   268  	projectName := comp.ProjectName()
   269  	t.Logf("projectName=%q", projectName)
   270  
   271  	base.Env = append(os.Environ(), "ADDRESS=0.0.0.0")
   272  
   273  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   274  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   275  
   276  	inspect := base.InspectContainer(containerName)
   277  	inspect80TCP := (*inspect.NetworkSettings.Ports)["80/tcp"]
   278  	expected := nat.PortBinding{
   279  		HostIP:   "0.0.0.0",
   280  		HostPort: "8080",
   281  	}
   282  	assert.Equal(base.T, expected, inspect80TCP[0])
   283  }
   284  
   285  func TestComposeUpDotEnvFile(t *testing.T) {
   286  	base := testutil.NewBase(t)
   287  
   288  	var dockerComposeYAML = `
   289  version: '3.1'
   290  
   291  services:
   292    svc3:
   293      image: ghcr.io/stargz-containers/nginx:$TAG
   294  `
   295  
   296  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   297  	defer comp.CleanUp()
   298  	projectName := comp.ProjectName()
   299  	t.Logf("projectName=%q", projectName)
   300  
   301  	envFile := `TAG=1.19-alpine-org`
   302  	comp.WriteFile(".env", envFile)
   303  
   304  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   305  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   306  }
   307  
   308  func TestComposeUpEnvFileNotFoundError(t *testing.T) {
   309  	base := testutil.NewBase(t)
   310  
   311  	var dockerComposeYAML = `
   312  version: '3.1'
   313  
   314  services:
   315    svc4:
   316      image: ghcr.io/stargz-containers/nginx:$TAG
   317  `
   318  
   319  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   320  	defer comp.CleanUp()
   321  	projectName := comp.ProjectName()
   322  	t.Logf("projectName=%q", projectName)
   323  
   324  	envFile := `TAG=1.19-alpine-org`
   325  	comp.WriteFile("envFile", envFile)
   326  
   327  	//env-file is relative to the current working directory and not the project directory
   328  	base.ComposeCmd("-f", comp.YAMLFullPath(), "--env-file", "envFile", "up", "-d").AssertFail()
   329  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   330  }
   331  
   332  func TestComposeUpWithScale(t *testing.T) {
   333  	base := testutil.NewBase(t)
   334  
   335  	var dockerComposeYAML = fmt.Sprintf(`
   336  version: '3.1'
   337  
   338  services:
   339    test:
   340      image: %s
   341      command: "sleep infinity"
   342  `, testutil.AlpineImage)
   343  
   344  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   345  	defer comp.CleanUp()
   346  	projectName := comp.ProjectName()
   347  	t.Logf("projectName=%q", projectName)
   348  
   349  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d", "--scale", "test=2").AssertOK()
   350  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   351  
   352  	base.ComposeCmd("-f", comp.YAMLFullPath(), "ps").AssertOutContains(serviceparser.DefaultContainerName(projectName, "test", "2"))
   353  }
   354  
   355  func TestComposeIPAMConfig(t *testing.T) {
   356  	base := testutil.NewBase(t)
   357  
   358  	var dockerComposeYAML = fmt.Sprintf(`
   359  version: '3.1'
   360  
   361  services:
   362    foo:
   363      image: %s
   364      command: "sleep infinity"
   365  
   366  networks:
   367    default:
   368      ipam:
   369        config:
   370          - subnet: 10.1.100.0/24
   371  `, testutil.AlpineImage)
   372  
   373  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   374  	defer comp.CleanUp()
   375  	projectName := comp.ProjectName()
   376  	t.Logf("projectName=%q", projectName)
   377  
   378  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   379  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   380  
   381  	base.Cmd("inspect", "-f", `{{json .NetworkSettings.Networks }}`, serviceparser.DefaultContainerName(projectName, "foo", "1")).AssertOutContains("10.1.100.")
   382  }
   383  
   384  func TestComposeUpRemoveOrphans(t *testing.T) {
   385  	base := testutil.NewBase(t)
   386  
   387  	var (
   388  		dockerComposeYAMLOrphan = fmt.Sprintf(`
   389  version: '3.1'
   390  
   391  services:
   392    test:
   393      image: %s
   394      command: "sleep infinity"
   395  `, testutil.AlpineImage)
   396  
   397  		dockerComposeYAMLFull = fmt.Sprintf(`
   398  %s
   399    orphan:
   400      image: %s
   401      command: "sleep infinity"
   402  `, dockerComposeYAMLOrphan, testutil.AlpineImage)
   403  	)
   404  
   405  	compOrphan := testutil.NewComposeDir(t, dockerComposeYAMLOrphan)
   406  	defer compOrphan.CleanUp()
   407  	compFull := testutil.NewComposeDir(t, dockerComposeYAMLFull)
   408  	defer compFull.CleanUp()
   409  
   410  	projectName := fmt.Sprintf("nerdctl-compose-test-%d", time.Now().Unix())
   411  	t.Logf("projectName=%q", projectName)
   412  
   413  	orphanContainer := serviceparser.DefaultContainerName(projectName, "orphan", "1")
   414  
   415  	base.ComposeCmd("-p", projectName, "-f", compFull.YAMLFullPath(), "up", "-d").AssertOK()
   416  	defer base.ComposeCmd("-p", projectName, "-f", compFull.YAMLFullPath(), "down", "-v").Run()
   417  	base.ComposeCmd("-p", projectName, "-f", compOrphan.YAMLFullPath(), "up", "-d").AssertOK()
   418  	base.ComposeCmd("-p", projectName, "-f", compFull.YAMLFullPath(), "ps").AssertOutContains(orphanContainer)
   419  	base.ComposeCmd("-p", projectName, "-f", compOrphan.YAMLFullPath(), "up", "-d", "--remove-orphans").AssertOK()
   420  	base.ComposeCmd("-p", projectName, "-f", compFull.YAMLFullPath(), "ps").AssertOutNotContains(orphanContainer)
   421  }
   422  
   423  func TestComposeUpIdempotent(t *testing.T) {
   424  	base := testutil.NewBase(t)
   425  
   426  	var dockerComposeYAML = fmt.Sprintf(`
   427  version: '3.1'
   428  
   429  services:
   430    test:
   431      image: %s
   432      command: "sleep infinity"
   433  `, testutil.AlpineImage)
   434  
   435  	comp := testutil.NewComposeDir(t, dockerComposeYAML)
   436  	defer comp.CleanUp()
   437  	projectName := comp.ProjectName()
   438  	t.Logf("projectName=%q", projectName)
   439  
   440  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   441  	defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
   442  	base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
   443  	base.ComposeCmd("-f", comp.YAMLFullPath(), "down").AssertOK()
   444  }
   445  
   446  func TestComposeUpWithExternalNetwork(t *testing.T) {
   447  	containerName1 := testutil.Identifier(t) + "-1"
   448  	containerName2 := testutil.Identifier(t) + "-2"
   449  	networkName := testutil.Identifier(t) + "-network"
   450  	var dockerComposeYaml1 = fmt.Sprintf(`
   451  version: "3"
   452  services:
   453    %s:
   454      image: %s
   455      container_name: %s
   456      networks:
   457        %s:
   458          aliases:
   459            - nginx-1
   460  networks:
   461    %s:
   462      external: true
   463  `, containerName1, testutil.NginxAlpineImage, containerName1, networkName, networkName)
   464  	var dockerComposeYaml2 = fmt.Sprintf(`
   465  version: "3"
   466  services:
   467    %s:
   468      image: %s
   469      container_name: %s
   470      networks:
   471        %s:
   472          aliases:
   473            - nginx-2
   474  networks:
   475    %s:
   476      external: true
   477  `, containerName2, testutil.NginxAlpineImage, containerName2, networkName, networkName)
   478  	comp1 := testutil.NewComposeDir(t, dockerComposeYaml1)
   479  	defer comp1.CleanUp()
   480  	comp2 := testutil.NewComposeDir(t, dockerComposeYaml2)
   481  	defer comp2.CleanUp()
   482  	base := testutil.NewBase(t)
   483  	// Create the test network
   484  	base.Cmd("network", "create", networkName).AssertOK()
   485  	defer base.Cmd("network", "rm", networkName).Run()
   486  	// Run the first compose
   487  	base.ComposeCmd("-f", comp1.YAMLFullPath(), "up", "-d").AssertOK()
   488  	defer base.ComposeCmd("-f", comp1.YAMLFullPath(), "down", "-v").Run()
   489  	// Run the second compose
   490  	base.ComposeCmd("-f", comp2.YAMLFullPath(), "up", "-d").AssertOK()
   491  	defer base.ComposeCmd("-f", comp2.YAMLFullPath(), "down", "-v").Run()
   492  	// Down the second compose
   493  	base.ComposeCmd("-f", comp2.YAMLFullPath(), "down", "-v").AssertOK()
   494  	// Run the second compose again
   495  	base.ComposeCmd("-f", comp2.YAMLFullPath(), "up", "-d").AssertOK()
   496  	base.Cmd("exec", containerName1, "wget", "-qO-", "http://"+containerName2).AssertOutContains(testutil.NginxAlpineIndexHTMLSnippet)
   497  }
   498  
   499  func TestComposeUpWithBypass4netns(t *testing.T) {
   500  	// docker does not support bypass4netns mode
   501  	testutil.DockerIncompatible(t)
   502  	if !rootlessutil.IsRootless() {
   503  		t.Skip("test needs rootless")
   504  	}
   505  	testutil.RequireKernelVersion(t, ">= 5.9.0-0")
   506  	testutil.RequireSystemService(t, "bypass4netnsd")
   507  	base := testutil.NewBase(t)
   508  	testComposeUp(t, base, fmt.Sprintf(`
   509  version: '3.1'
   510  
   511  services:
   512  
   513    wordpress:
   514      image: %s
   515      restart: always
   516      ports:
   517        - 8080:80
   518      environment:
   519        WORDPRESS_DB_HOST: db
   520        WORDPRESS_DB_USER: exampleuser
   521        WORDPRESS_DB_PASSWORD: examplepass
   522        WORDPRESS_DB_NAME: exampledb
   523      volumes:
   524        - wordpress:/var/www/html
   525      labels:
   526        - nerdctl/bypass4netns=1
   527  
   528    db:
   529      image: %s
   530      restart: always
   531      environment:
   532        MYSQL_DATABASE: exampledb
   533        MYSQL_USER: exampleuser
   534        MYSQL_PASSWORD: examplepass
   535        MYSQL_RANDOM_ROOT_PASSWORD: '1'
   536      volumes:
   537        - db:/var/lib/mysql
   538      labels:
   539        - nerdctl/bypass4netns=1
   540  
   541  volumes:
   542    wordpress:
   543    db:
   544  `, testutil.WordpressImage, testutil.MariaDBImage))
   545  }
   546  
   547  func TestComposeUpProfile(t *testing.T) {
   548  	base := testutil.NewBase(t)
   549  	serviceRegular := testutil.Identifier(t) + "-regular"
   550  	serviceProfiled := testutil.Identifier(t) + "-profiled"
   551  
   552  	dockerComposeYAML := fmt.Sprintf(`
   553  services:
   554    %s:
   555      image: %[3]s
   556  
   557    %[2]s:
   558      image: %[3]s
   559      profiles:
   560        - test-profile
   561  `, serviceRegular, serviceProfiled, testutil.NginxAlpineImage)
   562  
   563  	// * Test with profile
   564  	//   Should run both the services:
   565  	//     - matching active profile
   566  	//     - one without profile
   567  	comp1 := testutil.NewComposeDir(t, dockerComposeYAML)
   568  	defer comp1.CleanUp()
   569  	base.ComposeCmd("-f", comp1.YAMLFullPath(), "--profile", "test-profile", "up", "-d").AssertOK()
   570  
   571  	psCmd := base.Cmd("ps", "-a", "--format={{.Names}}")
   572  	psCmd.AssertOutContains(serviceRegular)
   573  	psCmd.AssertOutContains(serviceProfiled)
   574  	base.ComposeCmd("-f", comp1.YAMLFullPath(), "--profile", "test-profile", "down", "-v").AssertOK()
   575  
   576  	// * Test without profile
   577  	//   Should run:
   578  	//     - service without profile
   579  	comp2 := testutil.NewComposeDir(t, dockerComposeYAML)
   580  	defer comp2.CleanUp()
   581  	base.ComposeCmd("-f", comp2.YAMLFullPath(), "up", "-d").AssertOK()
   582  	defer base.ComposeCmd("-f", comp2.YAMLFullPath(), "down", "-v").AssertOK()
   583  
   584  	psCmd = base.Cmd("ps", "-a", "--format={{.Names}}")
   585  	psCmd.AssertOutContains(serviceRegular)
   586  	psCmd.AssertOutNotContains(serviceProfiled)
   587  }