Repository: docker/compose-cli
Pull request #1351 (2021-02-24 17:39:38)

Scan suggest message when compose build / compose up
**What I did**
* Add scan suggest message at the end of `docker compose build`, and end of build section when `docker compose up`
* check scan plugin is available before displaying message
* Allow users to turn this off setting `DOCKER_SCAN_SUGGEST = false`
* Do not display message if user has already used scan command

```
$ ./bin/docker compose -f ./local/e2e/compose/fixtures/build-test/compose.yml build
...
=> [build-test_nginx] exporting to image  
=> => exporting layers            
=> => writing image sha256:f727547da0e5aa30472c3705fd70bfe02ce300e50be0665eca05b6482bdd1aa9        
=> => naming to docker.io/gtardif/custom-nginx:1.0 
=> => naming to docker.io/library/build-test_nginx       
Try scanning the image you have just built to identify vulnerabilities with Docker’s new security tool: 
docker scan myorg/custom-nginx:1.0
docker scan build-test_nginx
$
```

and compose up: 
```
$ ./bin/docker compose -f ./local/e2e/compose/fixtures/build-test/compose.yml up -d
...
=> => writing image sha256:f727547da0e5aa30472c3705fd70bfe02ce300e50be0665eca05b6482bdd1aa9
=> => naming to docker.io/library/build-test_nginx                 
=> => naming to docker.io/library/custom-nginx    
Try scanning the image you have just built to identify vulnerabilities with Docker’s new security tool: 
docker scan build-test_nginx
docker scan custom-nginx
[+] Running 3/3
 ⠿ Network "build-test_default"   Created    
 ⠿ Container build-test_nginx_1   Started    
 ⠿ Container build-test_nginx2_1  Started   
$    
```

**Related issue**

<!-- optional tests
You can add a / mention to run tests executed by default only on main branch :
* `test-kube` to run Kube E2E tests
* `test-aci` to run ACI E2E tests
* `test-ecs` to run ECS E2E tests
* `test-windows` to run tests & E2E tests on windows
-->

**(not mandatory) A picture of a cute animal, if possible in relation with what you did**



Patch:
diff --git a/.gitignore b/.gitignore
index 48047887..a5d8f723 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
 bin/
 dist/
-/.vscode/
diff --git a/aci/backend.go b/aci/backend.go
index c9359c21..37332636 100644
--- a/aci/backend.go
+++ b/aci/backend.go
@@ -51,7 +51,6 @@ type LoginParams struct {
 	TenantID     string
 	ClientID     string
 	ClientSecret string
-	CloudName    string
 }
 
 // Validate returns an error if options are not used properly
diff --git a/aci/cloud.go b/aci/cloud.go
index d9b13b1a..d47c174a 100644
--- a/aci/cloud.go
+++ b/aci/cloud.go
@@ -25,7 +25,7 @@ import (
 )
 
 type aciCloudService struct {
-	loginService login.AzureLoginService
+	loginService login.AzureLoginServiceAPI
 }
 
 func (cs *aciCloudService) Login(ctx context.Context, params interface{}) error {
@@ -33,13 +33,10 @@ func (cs *aciCloudService) Login(ctx context.Context, params interface{}) error
 	if !ok {
 		return errors.New("could not read Azure LoginParams struct from generic parameter")
 	}
-	if opts.CloudName == "" {
-		opts.CloudName = login.AzurePublicCloudName
-	}
 	if opts.ClientID != "" {
-		return cs.loginService.LoginServicePrincipal(opts.ClientID, opts.ClientSecret, opts.TenantID, opts.CloudName)
+		return cs.loginService.LoginServicePrincipal(opts.ClientID, opts.ClientSecret, opts.TenantID)
 	}
-	return cs.loginService.Login(ctx, opts.TenantID, opts.CloudName)
+	return cs.loginService.Login(ctx, opts.TenantID)
 }
 
 func (cs *aciCloudService) Logout(ctx context.Context) error {
diff --git a/aci/compose.go b/aci/compose.go
index 8beba0ba..a417be19 100644
--- a/aci/compose.go
+++ b/aci/compose.go
@@ -175,7 +175,7 @@ func (cs *aciComposeService) Ps(ctx context.Context, projectName string, options
 	return res, nil
 }
 
-func (cs *aciComposeService) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
+func (cs *aciComposeService) List(ctx context.Context) ([]compose.Stack, error) {
 	containerGroups, err := getACIContainerGroups(ctx, cs.ctx.SubscriptionID, cs.ctx.ResourceGroup)
 	if err != nil {
 		return nil, err
diff --git a/aci/convert/registry_credentials.go b/aci/convert/registry_credentials.go
index 321c27b5..8919fb53 100644
--- a/aci/convert/registry_credentials.go
+++ b/aci/convert/registry_credentials.go
@@ -47,7 +47,7 @@ const (
 
 type registryHelper interface {
 	getAllRegistryCredentials() (map[string]types.AuthConfig, error)
-	autoLoginAcr(registry string, loginService login.AzureLoginService) error
+	autoLoginAcr(registry string) error
 }
 
 type cliRegistryHelper struct {
@@ -65,19 +65,9 @@ func newCliRegistryConfLoader() cliRegistryHelper {
 }
 
 func getRegistryCredentials(project compose.Project, helper registryHelper) ([]containerinstance.ImageRegistryCredential, error) {
-	loginService, err := login.NewAzureLoginService()
-	if err != nil {
-		return nil, err
-	}
-
-	var cloudEnvironment *login.CloudEnvironment = nil
-	if ce, err := loginService.GetCloudEnvironment(); err != nil {
-		cloudEnvironment = &ce
-	}
-
-	usedRegistries, acrRegistries := getUsedRegistries(project, cloudEnvironment)
+	usedRegistries, acrRegistries := getUsedRegistries(project)
 	for _, registry := range acrRegistries {
-		err := helper.autoLoginAcr(registry, loginService)
+		err := helper.autoLoginAcr(registry)
 		if err != nil {
 			fmt.Printf("WARNING: %v\n", err)
 			fmt.Printf("Could not automatically login to %s from your Azure login. Assuming you already logged in to the ACR registry\n", registry)
@@ -126,10 +116,9 @@ func getRegistryCredentials(project compose.Project, helper registryHelper) ([]c
 	return registryCreds, nil
 }
 
-func getUsedRegistries(project compose.Project, ce *login.CloudEnvironment) (map[string]bool, []string) {
+func getUsedRegistries(project compose.Project) (map[string]bool, []string) {
 	usedRegistries := map[string]bool{}
 	acrRegistries := []string{}
-
 	for _, service := range project.Services {
 		imageName := service.Image
 		tokens := strings.Split(imageName, "/")
@@ -138,18 +127,24 @@ func getUsedRegistries(project compose.Project, ce *login.CloudEnvironment) (map
 			registry = dockerHub
 		} else if !strings.Contains(registry, ".") {
 			registry = dockerHub
-		} else if ce != nil {
-			if suffix, present := ce.Suffixes[login.AcrSuffixKey]; present && strings.HasSuffix(registry, suffix) {
-				acrRegistries = append(acrRegistries, registry)
-			}
+		} else if strings.HasSuffix(registry, login.AcrRegistrySuffix) {
+			acrRegistries = append(acrRegistries, registry)
 		}
 		usedRegistries[registry] = true
 	}
 	return usedRegistries, acrRegistries
 }
 
-func (c cliRegistryHelper) autoLoginAcr(registry string, loginService login.AzureLoginService) error {
-	token, tenantID, err := loginService.GetValidToken()
+func (c cliRegistryHelper) autoLoginAcr(registry string) error {
+	loginService, err := login.NewAzureLoginService()
+	if err != nil {
+		return err
+	}
+	token, err := loginService.GetValidToken()
+	if err != nil {
+		return err
+	}
+	tenantID, err := loginService.GetTenantID()
 	if err != nil {
 		return err
 	}
diff --git a/aci/login/client.go b/aci/login/client.go
index 0d6bafd4..55b2fb97 100644
--- a/aci/login/client.go
+++ b/aci/login/client.go
@@ -17,8 +17,6 @@
 package login
 
 import (
-	"encoding/json"
-	"strconv"
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources"
@@ -26,8 +24,6 @@ import (
 	"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance"
 	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
 	"github.com/Azure/go-autorest/autorest"
-	"github.com/Azure/go-autorest/autorest/adal"
-	"github.com/Azure/go-autorest/autorest/date"
 	"github.com/pkg/errors"
 
 	"github.com/docker/compose-cli/api/errdefs"
@@ -36,12 +32,8 @@ import (
 
 // NewContainerGroupsClient get client toi manipulate containerGrouos
 func NewContainerGroupsClient(subscriptionID string) (containerinstance.ContainerGroupsClient, error) {
-	authorizer, mgmtURL, err := getClientSetupData()
-	if err != nil {
-		return containerinstance.ContainerGroupsClient{}, err
-	}
-	containerGroupsClient := containerinstance.NewContainerGroupsClientWithBaseURI(mgmtURL, subscriptionID)
-	setupClient(&containerGroupsClient.Client, authorizer)
+	containerGroupsClient := containerinstance.NewContainerGroupsClient(subscriptionID)
+	err := setupClient(&containerGroupsClient.Client)
 	if err != nil {
 		return containerinstance.ContainerGroupsClient{}, err
 	}
@@ -51,100 +43,68 @@ func NewContainerGroupsClient(subscriptionID string) (containerinstance.Containe
 	return containerGroupsClient, nil
 }
 
-func setupClient(aciClient *autorest.Client, auth autorest.Authorizer) {
+func setupClient(aciClient *autorest.Client) error {
 	aciClient.UserAgent = internal.UserAgentName + "/" + internal.Version
+	auth, err := NewAuthorizerFromLogin()
+	if err != nil {
+		return err
+	}
 	aciClient.Authorizer = auth
+	return nil
 }
 
 // NewStorageAccountsClient get client to manipulate storage accounts
 func NewStorageAccountsClient(subscriptionID string) (storage.AccountsClient, error) {
-	authorizer, mgmtURL, err := getClientSetupData()
+	containerGroupsClient := storage.NewAccountsClient(subscriptionID)
+	err := setupClient(&containerGroupsClient.Client)
 	if err != nil {
 		return storage.AccountsClient{}, err
 	}
-	storageAccuntsClient := storage.NewAccountsClientWithBaseURI(mgmtURL, subscriptionID)
-	setupClient(&storageAccuntsClient.Client, authorizer)
-	storageAccuntsClient.PollingDelay = 5 * time.Second
-	storageAccuntsClient.RetryAttempts = 30
-	storageAccuntsClient.RetryDuration = 1 * time.Second
-	return storageAccuntsClient, nil
+	containerGroupsClient.PollingDelay = 5 * time.Second
+	containerGroupsClient.RetryAttempts = 30
+	containerGroupsClient.RetryDuration = 1 * time.Second
+	return containerGroupsClient, nil
 }
 
 // NewFileShareClient get client to manipulate file shares
 func NewFileShareClient(subscriptionID string) (storage.FileSharesClient, error) {
-	authorizer, mgmtURL, err := getClientSetupData()
+	containerGroupsClient := storage.NewFileSharesClient(subscriptionID)
+	err := setupClient(&containerGroupsClient.Client)
 	if err != nil {
 		return storage.FileSharesClient{}, err
 	}
-	fileSharesClient := storage.NewFileSharesClientWithBaseURI(mgmtURL, subscriptionID)
-	setupClient(&fileSharesClient.Client, authorizer)
-	fileSharesClient.PollingDelay = 5 * time.Second
-	fileSharesClient.RetryAttempts = 30
-	fileSharesClient.RetryDuration = 1 * time.Second
-	return fileSharesClient, nil
+	containerGroupsClient.PollingDelay = 5 * time.Second
+	containerGroupsClient.RetryAttempts = 30
+	containerGroupsClient.RetryDuration = 1 * time.Second
+	return containerGroupsClient, nil
 }
 
 // NewSubscriptionsClient get subscription client
 func NewSubscriptionsClient() (subscription.SubscriptionsClient, error) {
-	authorizer, mgmtURL, err := getClientSetupData()
+	subc := subscription.NewSubscriptionsClient()
+	err := setupClient(&subc.Client)
 	if err != nil {
 		return subscription.SubscriptionsClient{}, errors.Wrap(errdefs.ErrLoginRequired, err.Error())
 	}
-	subc := subscription.NewSubscriptionsClientWithBaseURI(mgmtURL)
-	setupClient(&subc.Client, authorizer)
 	return subc, nil
 }
 
 // NewGroupsClient get client to manipulate groups
 func NewGroupsClient(subscriptionID string) (resources.GroupsClient, error) {
-	authorizer, mgmtURL, err := getClientSetupData()
+	groupsClient := resources.NewGroupsClient(subscriptionID)
+	err := setupClient(&groupsClient.Client)
 	if err != nil {
 		return resources.GroupsClient{}, err
 	}
-	groupsClient := resources.NewGroupsClientWithBaseURI(mgmtURL, subscriptionID)
-	setupClient(&groupsClient.Client, authorizer)
 	return groupsClient, nil
 }
 
 // NewContainerClient get client to manipulate containers
 func NewContainerClient(subscriptionID string) (containerinstance.ContainersClient, error) {
-	authorizer, mgmtURL, err := getClientSetupData()
+	containerClient := containerinstance.NewContainersClient(subscriptionID)
+	err := setupClient(&containerClient.Client)
 	if err != nil {
 		return containerinstance.ContainersClient{}, err
 	}
-	containerClient := containerinstance.NewContainersClientWithBaseURI(mgmtURL, subscriptionID)
-	setupClient(&containerClient.Client, authorizer)
 	return containerClient, nil
 }
-
-func getClientSetupData() (autorest.Authorizer, string, error) {
-	return getClientSetupDataImpl(GetTokenStorePath())
-}
-
-func getClientSetupDataImpl(tokenStorePath string) (autorest.Authorizer, string, error) {
-	als, err := newAzureLoginServiceFromPath(tokenStorePath, azureAPIHelper{}, CloudEnvironments)
-	if err != nil {
-		return nil, "", err
-	}
-
-	oauthToken, _, err := als.GetValidToken()
-	if err != nil {
-		return nil, "", errors.Wrap(err, "not logged in to azure, you need to run \"docker login azure\" first")
-	}
-
-	ce, err := als.GetCloudEnvironment()
-	if err != nil {
-		return nil, "", err
-	}
-
-	token := adal.Token{
-		AccessToken:  oauthToken.AccessToken,
-		Type:         oauthToken.TokenType,
-		ExpiresIn:    json.Number(strconv.Itoa(int(time.Until(oauthToken.Expiry).Seconds()))),
-		ExpiresOn:    json.Number(strconv.Itoa(int(oauthToken.Expiry.Sub(date.UnixEpoch()).Seconds()))),
-		RefreshToken: "",
-		Resource:     "",
-	}
-
-	return autorest.NewBearerAuthorizer(&token), ce.ResourceManagerURL, nil
-}
diff --git a/aci/login/cloud_environment.go b/aci/login/cloud_environment.go
deleted file mode 100644
index d02bef6f..00000000
--- a/aci/login/cloud_environment.go
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
-   Copyright 2020 Docker Compose CLI authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package login
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"os"
-	"strings"
-
-	"github.com/pkg/errors"
-)
-
-const (
-	// AzurePublicCloudName is the moniker of the Azure public cloud
-	AzurePublicCloudName = "AzureCloud"
-
-	// AcrSuffixKey is the well-known name of the DNS suffix for Azure Container Registries
-	AcrSuffixKey = "acrLoginServer"
-
-	// CloudMetadataURLVar is the name of the environment variable that (if defined), points to a URL that should be used by Docker CLI to retrieve cloud metadata
-	CloudMetadataURLVar = "ARM_CLOUD_METADATA_URL"
-
-	// DefaultCloudMetadataURL is the URL of the cloud metadata service maintained by Azure public cloud
-	DefaultCloudMetadataURL = "https://management.azure.com/metadata/endpoints?api-version=2019-05-01"
-)
-
-// CloudEnvironmentService exposed metadata about Azure cloud environments
-type CloudEnvironmentService interface {
-	Get(name string) (CloudEnvironment, error)
-}
-
-type cloudEnvironmentService struct {
-	cloudEnvironments map[string]CloudEnvironment
-	cloudMetadataURL  string
-	// True if we have queried the cloud metadata endpoint already.
-	// We do it only once per CLI invocation.
-	metadataQueried bool
-}
-
-var (
-	// CloudEnvironments is the default instance of the CloudEnvironmentService
-	CloudEnvironments CloudEnvironmentService
-)
-
-func init() {
-	CloudEnvironments = newCloudEnvironmentService()
-}
-
-// CloudEnvironmentAuthentication data for logging into, and obtaining tokens for, Azure sovereign clouds
-type CloudEnvironmentAuthentication struct {
-	LoginEndpoint string   `json:"loginEndpoint"`
-	Audiences     []string `json:"audiences"`
-	Tenant        string   `json:"tenant"`
-}
-
-// CloudEnvironment describes Azure sovereign cloud instance (e.g. Azure public, Azure US government, Azure China etc.)
-type CloudEnvironment struct {
-	Name               string                         `json:"name"`
-	Authentication     CloudEnvironmentAuthentication `json:"authentication"`
-	ResourceManagerURL string                         `json:"resourceManager"`
-	Suffixes           map[string]string              `json:"suffixes"`
-}
-
-func newCloudEnvironmentService() *cloudEnvironmentService {
-	retval := cloudEnvironmentService{
-		metadataQueried: false,
-	}
-	retval.resetCloudMetadata()
-	return &retval
-}
-
-func (ces *cloudEnvironmentService) Get(name string) (CloudEnvironment, error) {
-	if ce, present := ces.cloudEnvironments[name]; present {
-		return ce, nil
-	}
-
-	if !ces.metadataQueried {
-		ces.metadataQueried = true
-
-		if ces.cloudMetadataURL == "" {
-			ces.cloudMetadataURL = os.Getenv(CloudMetadataURLVar)
-			if _, err := url.ParseRequestURI(ces.cloudMetadataURL); err != nil {
-				ces.cloudMetadataURL = DefaultCloudMetadataURL
-			}
-		}
-
-		res, err := http.Get(ces.cloudMetadataURL)
-		if err != nil {
-			return CloudEnvironment{}, fmt.Errorf("Cloud metadata retrieval from '%s' failed: %w", ces.cloudMetadataURL, err)
-		}
-		if res.StatusCode != 200 {
-			return CloudEnvironment{}, errors.Errorf("Cloud metadata retrieval from '%s' failed: server response was '%s'", ces.cloudMetadataURL, res.Status)
-		}
-
-		bytes, err := ioutil.ReadAll(res.Body)
-		if err != nil {
-			return CloudEnvironment{}, fmt.Errorf("Cloud metadata retrieval from '%s' failed: %w", ces.cloudMetadataURL, err)
-		}
-
-		if err = ces.applyCloudMetadata(bytes); err != nil {
-			return CloudEnvironment{}, fmt.Errorf("Cloud metadata retrieval from '%s' failed: %w", ces.cloudMetadataURL, err)
-		}
-	}
-
-	if ce, present := ces.cloudEnvironments[name]; present {
-		return ce, nil
-	}
-
-	return CloudEnvironment{}, errors.Errorf("Cloud environment '%s' was not found", name)
-}
-
-func (ces *cloudEnvironmentService) applyCloudMetadata(jsonBytes []byte) error {
-	input := []CloudEnvironment{}
-	if err := json.Unmarshal(jsonBytes, &input); err != nil {
-		return err
-	}
-
-	newEnvironments := make(map[string]CloudEnvironment, len(input))
-	// If _any_ of the submitted data is invalid, we bail out.
-	for _, e := range input {
-		if len(e.Name) == 0 {
-			return errors.New("Azure cloud environment metadata is invalid (an environment with no name has been encountered)")
-		}
-
-		e.normalizeURLs()
-
-		if _, err := url.ParseRequestURI(e.Authentication.LoginEndpoint); err != nil {
-			return errors.Errorf("Metadata of cloud environment '%s' has invalid login endpoint URL: %s", e.Name, e.Authentication.LoginEndpoint)
-		}
-
-		if _, err := url.ParseRequestURI(e.ResourceManagerURL); err != nil {
-			return errors.Errorf("Metadata of cloud environment '%s' has invalid resource manager URL: %s", e.Name, e.ResourceManagerURL)
-		}
-
-		if len(e.Authentication.Audiences) == 0 {
-			return errors.Errorf("Metadata of cloud environment '%s' is invalid (no authentication audiences)", e.Name)
-		}
-
-		newEnvironments[e.Name] = e
-	}
-
-	for name, e := range newEnvironments {
-		ces.cloudEnvironments[name] = e
-	}
-	return nil
-}
-
-func (ces *cloudEnvironmentService) resetCloudMetadata() {
-	azurePublicCloud := CloudEnvironment{
-		Name: AzurePublicCloudName,
-		Authentication: CloudEnvironmentAuthentication{
-			LoginEndpoint: "https://login.microsoftonline.com",
-			Audiences: []string{
-				"https://management.core.windows.net",
-				"https://management.azure.com",
-			},
-			Tenant: "common",
-		},
-		ResourceManagerURL: "https://management.azure.com",
-		Suffixes: map[string]string{
-			AcrSuffixKey: "azurecr.io",
-		},
-	}
-
-	azureChinaCloud := CloudEnvironment{
-		Name: "AzureChinaCloud",
-		Authentication: CloudEnvironmentAuthentication{
-			LoginEndpoint: "https://login.chinacloudapi.cn",
-			Audiences: []string{
-				"https://management.core.chinacloudapi.cn",
-				"https://management.chinacloudapi.cn",
-			},
-			Tenant: "common",
-		},
-		ResourceManagerURL: "https://management.chinacloudapi.cn",
-		Suffixes: map[string]string{
-			AcrSuffixKey: "azurecr.cn",
-		},
-	}
-
-	azureUSGovernment := CloudEnvironment{
-		Name: "AzureUSGovernment",
-		Authentication: CloudEnvironmentAuthentication{
-			LoginEndpoint: "https://login.microsoftonline.us",
-			Audiences: []string{
-				"https://management.core.usgovcloudapi.net",
-				"https://management.usgovcloudapi.net",
-			},
-			Tenant: "common",
-		},
-		ResourceManagerURL: "https://management.usgovcloudapi.net",
-		Suffixes: map[string]string{
-			AcrSuffixKey: "azurecr.us",
-		},
-	}
-
-	azureGermanCloud := CloudEnvironment{
-		Name: "AzureGermanCloud",
-		Authentication: CloudEnvironmentAuthentication{
-			LoginEndpoint: "https://login.microsoftonline.de",
-			Audiences: []string{
-				"https://management.core.cloudapi.de",
-				"https://management.microsoftazure.de",
-			},
-			Tenant: "common",
-		},
-		ResourceManagerURL: "https://management.microsoftazure.de",
-
-		// There is no separate container registry suffix for German cloud
-		Suffixes: map[string]string{},
-	}
-
-	ces.cloudEnvironments = map[string]CloudEnvironment{
-		azurePublicCloud.Name:  azurePublicCloud,
-		azureChinaCloud.Name:   azureChinaCloud,
-		azureUSGovernment.Name: azureUSGovernment,
-		azureGermanCloud.Name:  azureGermanCloud,
-	}
-}
-
-// GetTenantQueryURL returns an URL that can be used to fetch the list of Azure Active Directory tenants from a given cloud environment
-func (ce *CloudEnvironment) GetTenantQueryURL() string {
-	tenantURL := ce.ResourceManagerURL + "/tenants?api-version=2019-11-01"
-	return tenantURL
-}
-
-// GetTokenScope returns a token scope that fits Docker CLI Azure management API usage
-func (ce *CloudEnvironment) GetTokenScope() string {
-	scope := "offline_access " + ce.ResourceManagerURL + "/.default"
-	return scope
-}
-
-// GetAuthorizeRequestFormat returns a string format that can be used to construct authorization code request in an OAuth2 flow.
-// The URL uses login endpoint appropriate for given cloud environment.
-func (ce *CloudEnvironment) GetAuthorizeRequestFormat() string {
-	authorizeFormat := ce.Authentication.LoginEndpoint + "/organizations/oauth2/v2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&state=%s&prompt=select_account&response_mode=query&scope=%s"
-	return authorizeFormat
-}
-
-// GetTokenRequestFormat returns a string format that can be used to construct a security token request against Azure Active Directory
-func (ce *CloudEnvironment) GetTokenRequestFormat() string {
-	tokenEndpoint := ce.Authentication.LoginEndpoint + "/%s/oauth2/v2.0/token"
-	return tokenEndpoint
-}
-
-func (ce *CloudEnvironment) normalizeURLs() {
-	ce.ResourceManagerURL = removeTrailingSlash(ce.ResourceManagerURL)
-	ce.Authentication.LoginEndpoint = removeTrailingSlash(ce.Authentication.LoginEndpoint)
-	for i, s := range ce.Authentication.Audiences {
-		ce.Authentication.Audiences[i] = removeTrailingSlash(s)
-	}
-}
-
-func removeTrailingSlash(s string) string {
-	return strings.TrimSuffix(s, "/")
-}
diff --git a/aci/login/helper.go b/aci/login/helper.go
index 2a5ec13f..4d20624a 100644
--- a/aci/login/helper.go
+++ b/aci/login/helper.go
@@ -39,17 +39,17 @@ var (
 )
 
 type apiHelper interface {
-	queryToken(ce CloudEnvironment, data url.Values, tenantID string) (azureToken, error)
-	openAzureLoginPage(redirectURL string, ce CloudEnvironment) error
+	queryToken(data url.Values, tenantID string) (azureToken, error)
+	openAzureLoginPage(redirectURL string) error
 	queryAPIWithHeader(ctx context.Context, authorizationURL string, authorizationHeader string) ([]byte, int, error)
-	getDeviceCodeFlowToken(ce CloudEnvironment) (adal.Token, error)
+	getDeviceCodeFlowToken() (adal.Token, error)
 }
 
 type azureAPIHelper struct{}
 
-func (helper azureAPIHelper) getDeviceCodeFlowToken(ce CloudEnvironment) (adal.Token, error) {
+func (helper azureAPIHelper) getDeviceCodeFlowToken() (adal.Token, error) {
 	deviceconfig := auth.NewDeviceFlowConfig(clientID, "common")
-	deviceconfig.Resource = ce.ResourceManagerURL
+	deviceconfig.Resource = azureManagementURL
 	spToken, err := deviceconfig.ServicePrincipalToken()
 	if err != nil {
 		return adal.Token{}, err
@@ -57,9 +57,9 @@ func (helper azureAPIHelper) getDeviceCodeFlowToken(ce CloudEnvironment) (adal.T
 	return spToken.Token(), err
 }
 
-func (helper azureAPIHelper) openAzureLoginPage(redirectURL string, ce CloudEnvironment) error {
+func (helper azureAPIHelper) openAzureLoginPage(redirectURL string) error {
 	state := randomString("", 10)
-	authURL := fmt.Sprintf(ce.GetAuthorizeRequestFormat(), clientID, redirectURL, state, ce.GetTokenScope())
+	authURL := fmt.Sprintf(authorizeFormat, clientID, redirectURL, state, scopes)
 	return openbrowser(authURL)
 }
 
@@ -81,8 +81,8 @@ func (helper azureAPIHelper) queryAPIWithHeader(ctx context.Context, authorizati
 	return bits, res.StatusCode, nil
 }
 
-func (helper azureAPIHelper) queryToken(ce CloudEnvironment, data url.Values, tenantID string) (azureToken, error) {
-	res, err := http.Post(fmt.Sprintf(ce.GetTokenRequestFormat(), tenantID), "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
+func (helper azureAPIHelper) queryToken(data url.Values, tenantID string) (azureToken, error) {
+	res, err := http.Post(fmt.Sprintf(tokenEndpoint, tenantID), "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
 	if err != nil {
 		return azureToken{}, err
 	}
diff --git a/aci/login/login.go b/aci/login/login.go
index 805b8423..21cb30fc 100644
--- a/aci/login/login.go
+++ b/aci/login/login.go
@@ -23,10 +23,13 @@ import (
 	"net/http"
 	"net/url"
 	"os"
+	"strconv"
 	"time"
 
+	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/adal"
 	"github.com/Azure/go-autorest/autorest/azure/auth"
+	"github.com/Azure/go-autorest/autorest/date"
 	"github.com/pkg/errors"
 	"golang.org/x/oauth2"
 
@@ -35,6 +38,18 @@ import (
 
 //go login process, derived from code sample provided by MS at https://github.com/devigned/go-az-cli-stuff
 const (
+	// AcrRegistrySuffix suffix for ACR registry images
+	AcrRegistrySuffix         = ".azurecr.io"
+	activeDirectoryURL        = "https://login.microsoftonline.com"
+	azureManagementURL        = "https://management.core.windows.net/"
+	azureResouceManagementURL = "https://management.azure.com/"
+	authorizeFormat           = activeDirectoryURL + "/organizations/oauth2/v2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&state=%s&prompt=select_account&response_mode=query&scope=%s"
+	tokenEndpoint             = activeDirectoryURL + "/%s/oauth2/v2.0/token"
+	getTenantURL              = azureResouceManagementURL + "tenants?api-version=2019-11-01"
+
+	// scopes for a multi-tenant app works for openid, email, other common scopes, but fails when trying to add a token
+	// v1 scope like "https://management.azure.com/.default" for ARM access
+	scopes   = "offline_access " + azureResouceManagementURL + ".default"
 	clientID = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" // Azure CLI client id
 )
 
@@ -58,41 +73,39 @@ type (
 )
 
 // AzureLoginService Service to log into azure and get authentifier for azure APIs
-type AzureLoginService interface {
-	Login(ctx context.Context, requestedTenantID string, cloudEnvironment string) error
-	LoginServicePrincipal(clientID string, clientSecret string, tenantID string, cloudEnvironment string) error
-	Logout(ctx context.Context) error
-	GetCloudEnvironment() (CloudEnvironment, error)
-	GetValidToken() (oauth2.Token, string, error)
+type AzureLoginService struct {
+	tokenStore tokenStore
+	apiHelper  apiHelper
 }
-type azureLoginService struct {
-	tokenStore          tokenStore
-	apiHelper           apiHelper
-	cloudEnvironmentSvc CloudEnvironmentService
+
+// AzureLoginServiceAPI interface for Azure login service
+type AzureLoginServiceAPI interface {
+	LoginServicePrincipal(clientID string, clientSecret string, tenantID string) error
+	Login(ctx context.Context, requestedTenantID string) error
+	Logout(ctx context.Context) error
 }
 
 const tokenStoreFilename = "dockerAccessToken.json"
 
 // NewAzureLoginService creates a NewAzureLoginService
-func NewAzureLoginService() (AzureLoginService, error) {
-	return newAzureLoginServiceFromPath(GetTokenStorePath(), azureAPIHelper{}, CloudEnvironments)
+func NewAzureLoginService() (*AzureLoginService, error) {
+	return newAzureLoginServiceFromPath(GetTokenStorePath(), azureAPIHelper{})
 }
 
-func newAzureLoginServiceFromPath(tokenStorePath string, helper apiHelper, ces CloudEnvironmentService) (*azureLoginService, error) {
+func newAzureLoginServiceFromPath(tokenStorePath string, helper apiHelper) (*AzureLoginService, error) {
 	store, err := newTokenStore(tokenStorePath)
 	if err != nil {
 		return nil, err
 	}
-	return &azureLoginService{
-		tokenStore:          store,
-		apiHelper:           helper,
-		cloudEnvironmentSvc: ces,
+	return &AzureLoginService{
+		tokenStore: store,
+		apiHelper:  helper,
 	}, nil
 }
 
 // LoginServicePrincipal login with clientId / clientSecret from a service principal.
 // The resulting token does not include a refresh token
-func (login *azureLoginService) LoginServicePrincipal(clientID string, clientSecret string, tenantID string, cloudEnvironment string) error {
+func (login *AzureLoginService) LoginServicePrincipal(clientID string, clientSecret string, tenantID string) error {
 	// Tried with auth2.NewUsernamePasswordConfig() but could not make this work with username / password, setting this for CI with clientID / clientSecret
 	creds := auth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
 
@@ -108,7 +121,7 @@ func (login *azureLoginService) LoginServicePrincipal(clientID string, clientSec
 	if err != nil {
 		return errors.Wrapf(errdefs.ErrLoginFailed, "could not read service principal token expiry: %s", err)
 	}
-	loginInfo := TokenInfo{TenantID: tenantID, Token: token, CloudEnvironment: cloudEnvironment}
+	loginInfo := TokenInfo{TenantID: tenantID, Token: token}
 
 	if err := login.tokenStore.writeLoginInfo(loginInfo); err != nil {
 		return errors.Wrapf(errdefs.ErrLoginFailed, "could not store login info: %s", err)
@@ -117,7 +130,7 @@ func (login *azureLoginService) LoginServicePrincipal(clientID string, clientSec
 }
 
 // Logout remove azure token data
-func (login *azureLoginService) Logout(ctx context.Context) error {
+func (login *AzureLoginService) Logout(ctx context.Context) error {
 	err := login.tokenStore.removeData()
 	if os.IsNotExist(err) {
 		return errors.New("No Azure login data to be removed")
@@ -125,14 +138,8 @@ func (login *azureLoginService) Logout(ctx context.Context) error {
 	return err
 }
 
-func (login *azureLoginService) getTenantAndValidateLogin(
-	ctx context.Context,
-	accessToken string,
-	refreshToken string,
-	requestedTenantID string,
-	ce CloudEnvironment,
-) error {
-	bits, statusCode, err := login.apiHelper.queryAPIWithHeader(ctx, ce.GetTenantQueryURL(), fmt.Sprintf("Bearer %s", accessToken))
+func (login *AzureLoginService) getTenantAndValidateLogin(ctx context.Context, accessToken string, refreshToken string, requestedTenantID string) error {
+	bits, statusCode, err := login.apiHelper.queryAPIWithHeader(ctx, getTenantURL, fmt.Sprintf("Bearer %s", accessToken))
 	if err != nil {
 		return errors.Wrapf(errdefs.ErrLoginFailed, "check auth failed: %s", err)
 	}
@@ -148,11 +155,11 @@ func (login *azureLoginService) getTenantAndValidateLogin(
 	if err != nil {
 		return errors.Wrap(errdefs.ErrLoginFailed, err.Error())
 	}
-	tToken, err := login.refreshToken(refreshToken, tenantID, ce)
+	tToken, err := login.refreshToken(refreshToken, tenantID)
 	if err != nil {
 		return errors.Wrapf(errdefs.ErrLoginFailed, "unable to refresh token: %s", err)
 	}
-	loginInfo := TokenInfo{TenantID: tenantID, Token: tToken, CloudEnvironment: ce.Name}
+	loginInfo := TokenInfo{TenantID: tenantID, Token: tToken}
 
 	if err := login.tokenStore.writeLoginInfo(loginInfo); err != nil {
 		return errors.Wrapf(errdefs.ErrLoginFailed, "could not store login info: %s", err)
@@ -161,12 +168,7 @@ func (login *azureLoginService) getTenantAndValidateLogin(
 }
 
 // Login performs an Azure login through a web browser
-func (login *azureLoginService) Login(ctx context.Context, requestedTenantID string, cloudEnvironment string) error {
-	ce, err := login.cloudEnvironmentSvc.Get(cloudEnvironment)
-	if err != nil {
-		return err
-	}
-
+func (login *AzureLoginService) Login(ctx context.Context, requestedTenantID string) error {
 	queryCh := make(chan localResponse, 1)
 	s, err := NewLocalServer(queryCh)
 	if err != nil {
@@ -181,8 +183,8 @@ func (login *azureLoginService) Login(ctx context.Context, requestedTenantID str
 	}
 
 	deviceCodeFlowCh := make(chan deviceCodeFlowResponse, 1)
-	if err = login.apiHelper.openAzureLoginPage(redirectURL, ce); err != nil {
-		login.startDeviceCodeFlow(deviceCodeFlowCh, ce)
+	if err = login.apiHelper.openAzureLoginPage(redirectURL); err != nil {
+		login.startDeviceCodeFlow(deviceCodeFlowCh)
 	}
 
 	select {
@@ -193,7 +195,7 @@ func (login *azureLoginService) Login(ctx context.Context, requestedTenantID str
 			return errors.Wrapf(errdefs.ErrLoginFailed, "could not get token using device code flow: %s", err)
 		}
 		token := dcft.token
-		return login.getTenantAndValidateLogin(ctx, token.AccessToken, token.RefreshToken, requestedTenantID, ce)
+		return login.getTenantAndValidateLogin(ctx, token.AccessToken, token.RefreshToken, requestedTenantID)
 	case q := <-queryCh:
 		if q.err != nil {
 			return errors.Wrapf(errdefs.ErrLoginFailed, "unhandled local login server error: %s", err)
@@ -206,14 +208,14 @@ func (login *azureLoginService) Login(ctx context.Context, requestedTenantID str
 			"grant_type":   []string{"authorization_code"},
 			"client_id":    []string{clientID},
 			"code":         code,
-			"scope":        []string{ce.GetTokenScope()},
+			"scope":        []string{scopes},
 			"redirect_uri": []string{redirectURL},
 		}
-		token, err := login.apiHelper.queryToken(ce, data, "organizations")
+		token, err := login.apiHelper.queryToken(data, "organizations")
 		if err != nil {
 			return errors.Wrapf(errdefs.ErrLoginFailed, "access token request failed: %s", err)
 		}
-		return login.getTenantAndValidateLogin(ctx, token.AccessToken, token.RefreshToken, requestedTenantID, ce)
+		return login.getTenantAndValidateLogin(ctx, token.AccessToken, token.RefreshToken, requestedTenantID)
 	}
 }
 
@@ -222,10 +224,10 @@ type deviceCodeFlowResponse struct {
 	err   error
 }
 
-func (login *azureLoginService) startDeviceCodeFlow(deviceCodeFlowCh chan deviceCodeFlowResponse, ce CloudEnvironment) {
+func (login *AzureLoginService) startDeviceCodeFlow(deviceCodeFlowCh chan deviceCodeFlowResponse) {
 	fmt.Println("Could not automatically open a browser, falling back to Azure device code flow authentication")
 	go func() {
-		token, err := login.apiHelper.getDeviceCodeFlowToken(ce)
+		token, err := login.apiHelper.getDeviceCodeFlowToken()
 		if err != nil {
 			deviceCodeFlowCh <- deviceCodeFlowResponse{err: err}
 		}
@@ -274,58 +276,72 @@ func spToOAuthToken(token adal.Token) (oauth2.Token, error) {
 	return oauthToken, nil
 }
 
-// GetValidToken returns an access token and associated tenant ID.
-// Will refresh the token as necessary.
-func (login *azureLoginService) GetValidToken() (oauth2.Token, string, error) {
-	loginInfo, err := login.tokenStore.readToken()
+// NewAuthorizerFromLogin creates an authorizer based on login access token
+func NewAuthorizerFromLogin() (autorest.Authorizer, error) {
+	return newAuthorizerFromLoginStorePath(GetTokenStorePath())
+}
+
+func newAuthorizerFromLoginStorePath(storeTokenPath string) (autorest.Authorizer, error) {
+	login, err := newAzureLoginServiceFromPath(storeTokenPath, azureAPIHelper{})
 	if err != nil {
-		return oauth2.Token{}, "", err
-	}
-	token := loginInfo.Token
-	tenantID := loginInfo.TenantID
-	if token.Valid() {
-		return token, tenantID, nil
+		return nil, err
 	}
-
-	ce, err := login.cloudEnvironmentSvc.Get(loginInfo.CloudEnvironment)
+	oauthToken, err := login.GetValidToken()
 	if err != nil {
-		return oauth2.Token{}, "", errors.Wrap(err, "access token request failed--cloud environment could not be determined.")
+		return nil, errors.Wrap(err, "not logged in to azure, you need to run \"docker login azure\" first")
 	}
 
-	token, err = login.refreshToken(token.RefreshToken, tenantID, ce)
-	if err != nil {
-		return oauth2.Token{}, "", errors.Wrap(err, "access token request failed. Maybe you need to login to Azure again.")
+	token := adal.Token{
+		AccessToken:  oauthToken.AccessToken,
+		Type:         oauthToken.TokenType,
+		ExpiresIn:    json.Number(strconv.Itoa(int(time.Until(oauthToken.Expiry).Seconds()))),
+		ExpiresOn:    json.Number(strconv.Itoa(int(oauthToken.Expiry.Sub(date.UnixEpoch()).Seconds()))),
+		RefreshToken: "",
+		Resource:     "",
 	}
-	err = login.tokenStore.writeLoginInfo(TokenInfo{TenantID: tenantID, Token: token, CloudEnvironment: ce.Name})
+
+	return autorest.NewBearerAuthorizer(&token), nil
+}
+
+// GetTenantID returns tenantID for current login
+func (login AzureLoginService) GetTenantID() (string, error) {
+	loginInfo, err := login.tokenStore.readToken()
 	if err != nil {
-		return oauth2.Token{}, "", err
+		return "", err
 	}
-	return token, tenantID, nil
+	return loginInfo.TenantID, err
 }
 
-// GeCloudEnvironment returns the cloud environment associated with the current authentication token (if we have one)
-func (login *azureLoginService) GetCloudEnvironment() (CloudEnvironment, error) {
-	tokenInfo, err := login.tokenStore.readToken()
+// GetValidToken returns an access token. Refresh token if needed
+func (login *AzureLoginService) GetValidToken() (oauth2.Token, error) {
+	loginInfo, err := login.tokenStore.readToken()
 	if err != nil {
-		return CloudEnvironment{}, err
+		return oauth2.Token{}, err
 	}
-
-	cloudEnvironment, err := login.cloudEnvironmentSvc.Get(tokenInfo.CloudEnvironment)
+	token := loginInfo.Token
+	if token.Valid() {
+		return token, nil
+	}
+	tenantID := loginInfo.TenantID
+	token, err = login.refreshToken(token.RefreshToken, tenantID)
 	if err != nil {
-		return CloudEnvironment{}, err
+		return oauth2.Token{}, errors.Wrap(err, "access token request failed. Maybe you need to login to azure again.")
 	}
-
-	return cloudEnvironment, nil
+	err = login.tokenStore.writeLoginInfo(TokenInfo{TenantID: tenantID, Token: token})
+	if err != nil {
+		return oauth2.Token{}, err
+	}
+	return token, nil
 }
 
-func (login *azureLoginService) refreshToken(currentRefreshToken string, tenantID string, ce CloudEnvironment) (oauth2.Token, error) {
+func (login *AzureLoginService) refreshToken(currentRefreshToken string, tenantID string) (oauth2.Token, error) {
 	data := url.Values{
 		"grant_type":    []string{"refresh_token"},
 		"client_id":     []string{clientID},
-		"scope":         []string{ce.GetTokenScope()},
+		"scope":         []string{scopes},
 		"refresh_token": []string{currentRefreshToken},
 	}
-	token, err := login.apiHelper.queryToken(ce, data, tenantID)
+	token, err := login.apiHelper.queryToken(data, tenantID)
 	if err != nil {
 		return oauth2.Token{}, err
 	}
diff --git a/aci/login/token_store.go b/aci/login/token_store.go
index 13100688..337e9b93 100644
--- a/aci/login/token_store.go
+++ b/aci/login/token_store.go
@@ -34,9 +34,8 @@ type tokenStore struct {
 
 // TokenInfo data stored in tokenStore
 type TokenInfo struct {
-	Token            oauth2.Token `json:"oauthToken"`
-	TenantID         string       `json:"tenantId"`
-	CloudEnvironment string       `json:"cloudEnvironment"`
+	Token    oauth2.Token `json:"oauthToken"`
+	TenantID string       `json:"tenantId"`
 }
 
 func newTokenStore(path string) (tokenStore, error) {
@@ -83,9 +82,6 @@ func (store tokenStore) readToken() (TokenInfo, error) {
 	if err := json.Unmarshal(bytes, &loginInfo); err != nil {
 		return TokenInfo{}, err
 	}
-	if loginInfo.CloudEnvironment == "" {
-		loginInfo.CloudEnvironment = AzurePublicCloudName
-	}
 	return loginInfo, nil
 }
 
diff --git a/api/client/compose.go b/api/client/compose.go
index 75e7be16..29373e47 100644
--- a/api/client/compose.go
+++ b/api/client/compose.go
@@ -68,7 +68,7 @@ func (c *composeService) Ps(context.Context, string, compose.PsOptions) ([]compo
 	return nil, errdefs.ErrNotImplemented
 }
 
-func (c *composeService) List(context.Context, compose.ListOptions) ([]compose.Stack, error) {
+func (c *composeService) List(context.Context) ([]compose.Stack, error) {
 	return nil, errdefs.ErrNotImplemented
 }
 
diff --git a/api/compose/api.go b/api/compose/api.go
index cd9dc4d8..c68b5e93 100644
--- a/api/compose/api.go
+++ b/api/compose/api.go
@@ -48,7 +48,7 @@ type Service interface {
 	// Ps executes the equivalent to a `compose ps`
 	Ps(ctx context.Context, projectName string, options PsOptions) ([]ContainerSummary, error)
 	// List executes the equivalent to a `docker stack ls`
-	List(ctx context.Context, options ListOptions) ([]Stack, error)
+	List(ctx context.Context) ([]Stack, error)
 	// Convert translate compose model into backend's native format
 	Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
 	// Kill executes the equivalent to a `compose kill`
@@ -165,11 +165,6 @@ func (opts *RunOptions) EnvironmentMap() types.MappingWithEquals {
 	return environment
 }
 
-// ListOptions group options of the ls API
-type ListOptions struct {
-	All bool
-}
-
 // PsOptions group options of the Ps API
 type PsOptions struct {
 	All bool
diff --git a/cli/cmd/compose/compose.go b/cli/cmd/compose/compose.go
index 7cf9b0c1..5bb63cd9 100644
--- a/cli/cmd/compose/compose.go
+++ b/cli/cmd/compose/compose.go
@@ -113,7 +113,7 @@ func Command(contextType string) *cobra.Command {
 		startCommand(&opts),
 		stopCommand(&opts),
 		psCommand(&opts),
-		listCommand(contextType),
+		listCommand(),
 		logsCommand(&opts, contextType),
 		convertCommand(&opts),
 		killCommand(&opts),
diff --git a/cli/cmd/compose/list.go b/cli/cmd/compose/list.go
index 54f8d1d4..b45b8f2a 100644
--- a/cli/cmd/compose/list.go
+++ b/cli/cmd/compose/list.go
@@ -28,18 +28,16 @@ import (
 
 	"github.com/docker/compose-cli/api/client"
 	"github.com/docker/compose-cli/api/compose"
-	"github.com/docker/compose-cli/api/context/store"
 	"github.com/docker/compose-cli/cli/formatter"
 )
 
 type lsOptions struct {
 	Format string
 	Quiet  bool
-	All    bool
 	Filter opts.FilterOpt
 }
 
-func listCommand(contextType string) *cobra.Command {
+func listCommand() *cobra.Command {
 	opts := lsOptions{Filter: opts.NewFilterOpt()}
 	lsCmd := &cobra.Command{
 		Use:   "ls",
@@ -51,9 +49,6 @@ func listCommand(contextType string) *cobra.Command {
 	lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].")
 	lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs.")
 	lsCmd.Flags().Var(&opts.Filter, "filter", "Filter output based on conditions provided.")
-	if contextType == store.DefaultContextType {
-		lsCmd.Flags().BoolVarP(&opts.All, "all", "a", false, "Show all stopped Compose projects")
-	}
 
 	return lsCmd
 }
@@ -73,7 +68,7 @@ func runList(ctx context.Context, opts lsOptions) error {
 	if err != nil {
 		return err
 	}
-	stackList, err := c.ComposeService().List(ctx, compose.ListOptions{All: opts.All})
+	stackList, err := c.ComposeService().List(ctx)
 	if err != nil {
 		return err
 	}
diff --git a/cli/cmd/login/azurelogin.go b/cli/cmd/login/azurelogin.go
index ef6d687c..fd90170c 100644
--- a/cli/cmd/login/azurelogin.go
+++ b/cli/cmd/login/azurelogin.go
@@ -40,7 +40,6 @@ func AzureLoginCommand() *cobra.Command {
 	flags.StringVar(&opts.TenantID, "tenant-id", "", "Specify tenant ID to use")
 	flags.StringVar(&opts.ClientID, "client-id", "", "Client ID for Service principal login")
 	flags.StringVar(&opts.ClientSecret, "client-secret", "", "Client secret for Service principal login")
-	flags.StringVar(&opts.CloudName, "cloud-name", "", "Name of a registered Azure cloud")
 
 	return cmd
 }
diff --git a/cli/server/protos/compose/v1/compose.pb.go b/cli/server/protos/compose/v1/compose.pb.go
index b1affb9e..ba8d408c 100644
--- a/cli/server/protos/compose/v1/compose.pb.go
+++ b/cli/server/protos/compose/v1/compose.pb.go
@@ -16,7 +16,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.13.0
+// 	protoc        v3.12.2
 // source: cli/server/protos/compose/v1/compose.proto
 
 package v1
@@ -270,7 +270,6 @@ type ComposeStacksRequest struct {
 	unknownFields protoimpl.UnknownFields
 
 	ProjectName string `protobuf:"bytes,1,opt,name=projectName,proto3" json:"projectName,omitempty"`
-	All         bool   `protobuf:"varint,2,opt,name=all,proto3" json:"all,omitempty"`
 }
 
 func (x *ComposeStacksRequest) Reset() {
@@ -312,13 +311,6 @@ func (x *ComposeStacksRequest) GetProjectName() string {
 	return ""
 }
 
-func (x *ComposeStacksRequest) GetAll() bool {
-	if x != nil {
-		return x.All
-	}
-	return false
-}
-
 type ComposeStacksResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -653,79 +645,78 @@ var file_cli_server_protos_compose_v1_compose_proto_rawDesc = []byte{
 	0x6c, 0x65, 0x73, 0x22, 0x37, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x44, 0x6f,
 	0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72,
 	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x4a, 0x0a, 0x14,
+	0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x38, 0x0a, 0x14,
 	0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71,
 	0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e,
 	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65,
-	0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x58, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70,
-	0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61,
-	0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
-	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x52, 0x06, 0x73, 0x74, 0x61, 0x63,
-	0x6b, 0x73, 0x22, 0x5b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69,
-	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-	0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f,
-	0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22,
-	0x6a, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f,
-	0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
-	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77,
-	0x6f, 0x72, 0x6b, 0x44, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x77, 0x6f,
-	0x72, 0x6b, 0x44, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x60, 0x0a, 0x17, 0x43,
-	0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64,
-	0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76,
-	0x69, 0x63, 0x65, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x79, 0x0a,
-	0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08,
-	0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08,
-	0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x73, 0x69,
-	0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x65, 0x73, 0x69, 0x72,
-	0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
-	0x09, 0x52, 0x05, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x32, 0xe9, 0x03, 0x0a, 0x07, 0x43, 0x6f, 0x6d,
-	0x70, 0x6f, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6d,
-	0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f,
-	0x6d, 0x70, 0x6f, 0x73, 0x65, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76,
-	0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x34, 0x2e, 0x63, 0x6f,
-	0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43,
-	0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61,
-	0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
-	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x44, 0x6f, 0x77, 0x6e,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x63,
-	0x6b, 0x73, 0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e,
-	0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f,
-	0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61,
-	0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, 0x6f, 0x6d,
-	0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f,
-	0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x12, 0x7f, 0x0a, 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12,
-	0x38, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
+	0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x58, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
+	0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x27, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
 	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e,
-	0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x63, 0x6f, 0x6d, 0x2e,
+	0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x52, 0x06, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x73,
+	0x22, 0x5b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x6a, 0x0a,
+	0x16, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72,
+	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x6f, 0x72,
+	0x6b, 0x44, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x77, 0x6f, 0x72, 0x6b,
+	0x44, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x60, 0x0a, 0x17, 0x43, 0x6f, 0x6d,
+	0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63,
+	0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63,
+	0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x79, 0x0a, 0x07, 0x53,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65,
+	0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65,
+	0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65,
+	0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64,
+	0x12, 0x14, 0x0a, 0x05, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x05, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x32, 0xe9, 0x03, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
+	0x73, 0x65, 0x12, 0x6d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64,
+	0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+	0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
+	0x6f, 0x73, 0x65, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e,
+	0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x73, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6d, 0x2e,
 	0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 	0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d,
-	0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
-	0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73,
-	0x65, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
-	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2f,
-	0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x70, 0x6f, 0x73, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x35, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e,
+	0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x73,
+	0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70,
+	0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65,
+	0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b,
+	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64,
+	0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
+	0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
+	0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x7f, 0x0a, 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x38, 0x2e,
+	0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31,
+	0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x6f,
+	0x63, 0x6b, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
+	0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
+	0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+	0x2f, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2d,
+	0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x65, 0x2f, 0x76, 0x31,
+	0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/cli/server/protos/compose/v1/compose.proto b/cli/server/protos/compose/v1/compose.proto
index 81a088e8..2858af16 100644
--- a/cli/server/protos/compose/v1/compose.proto
+++ b/cli/server/protos/compose/v1/compose.proto
@@ -48,7 +48,6 @@ message ComposeDownResponse {
 
 message ComposeStacksRequest {
   string projectName = 1;
-  bool all = 2;
 }
 
 message ComposeStacksResponse {
diff --git a/cli/server/protos/containers/v1/containers.pb.go b/cli/server/protos/containers/v1/containers.pb.go
index 32a88513..6b98ce33 100644
--- a/cli/server/protos/containers/v1/containers.pb.go
+++ b/cli/server/protos/containers/v1/containers.pb.go
@@ -16,7 +16,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.13.0
+// 	protoc        v3.12.2
 // source: cli/server/protos/containers/v1/containers.proto
 
 package v1
diff --git a/cli/server/protos/contexts/v1/contexts.pb.go b/cli/server/protos/contexts/v1/contexts.pb.go
index 7153570c..6014e429 100644
--- a/cli/server/protos/contexts/v1/contexts.pb.go
+++ b/cli/server/protos/contexts/v1/contexts.pb.go
@@ -16,7 +16,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.13.0
+// 	protoc        v3.12.2
 // source: cli/server/protos/contexts/v1/contexts.proto
 
 package v1
diff --git a/cli/server/protos/streams/v1/streams.pb.go b/cli/server/protos/streams/v1/streams.pb.go
index bfc775cc..a27c8ce9 100644
--- a/cli/server/protos/streams/v1/streams.pb.go
+++ b/cli/server/protos/streams/v1/streams.pb.go
@@ -16,7 +16,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.13.0
+// 	protoc        v3.12.2
 // source: cli/server/protos/streams/v1/streams.proto
 
 package v1
diff --git a/cli/server/protos/volumes/v1/volumes.pb.go b/cli/server/protos/volumes/v1/volumes.pb.go
index 22e15063..43582976 100644
--- a/cli/server/protos/volumes/v1/volumes.pb.go
+++ b/cli/server/protos/volumes/v1/volumes.pb.go
@@ -16,7 +16,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.13.0
+// 	protoc        v3.12.2
 // source: cli/server/protos/volumes/v1/volumes.proto
 
 package v1
diff --git a/cli/server/proxy/compose.go b/cli/server/proxy/compose.go
index fa3f914f..0fac7cc9 100644
--- a/cli/server/proxy/compose.go
+++ b/cli/server/proxy/compose.go
@@ -77,7 +77,7 @@ func (p *proxy) Services(ctx context.Context, request *composev1.ComposeServices
 }
 
 func (p *proxy) Stacks(ctx context.Context, request *composev1.ComposeStacksRequest) (*composev1.ComposeStacksResponse, error) {
-	stacks, err := Client(ctx).ComposeService().List(ctx, compose.ListOptions{All: request.All})
+	stacks, err := Client(ctx).ComposeService().List(ctx)
 	if err != nil {
 		return nil, err
 	}
diff --git a/ecs/list.go b/ecs/list.go
index 7af738da..e54e780c 100644
--- a/ecs/list.go
+++ b/ecs/list.go
@@ -23,7 +23,7 @@ import (
 	"github.com/docker/compose-cli/api/compose"
 )
 
-func (b *ecsAPIService) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
+func (b *ecsAPIService) List(ctx context.Context) ([]compose.Stack, error) {
 	stacks, err := b.aws.ListStacks(ctx)
 	if err != nil {
 		return nil, err
diff --git a/ecs/local/compose.go b/ecs/local/compose.go
index 6ecce3e7..9600a336 100644
--- a/ecs/local/compose.go
+++ b/ecs/local/compose.go
@@ -169,8 +169,8 @@ func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, consum
 func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string, options compose.PsOptions) ([]compose.ContainerSummary, error) {
 	return e.compose.Ps(ctx, projectName, options)
 }
-func (e ecsLocalSimulation) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
-	return e.compose.List(ctx, opts)
+func (e ecsLocalSimulation) List(ctx context.Context) ([]compose.Stack, error) {
+	return e.compose.List(ctx)
 }
 func (e ecsLocalSimulation) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
 	return 0, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose run")
diff --git a/go.sum b/go.sum
index 2e413434..4ac8ac24 100644
--- a/go.sum
+++ b/go.sum
@@ -185,6 +185,7 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU
 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
 github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
 github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
@@ -271,6 +272,7 @@ github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMS
 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
 github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
 github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -291,6 +293,7 @@ github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY=
 github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
+github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiKw=
 github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bURvoVUSjTo=
 github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4=
 github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
@@ -485,6 +488,7 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
 github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
 github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko=
 github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
@@ -690,6 +694,7 @@ github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Z
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
 github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
 github.com/google/crfs v0.0.0-20191108021818-71d77da419c9/go.mod h1:etGhoOqfwPkooV6aqoX3eBGQOJblqdoc9XvWOeuxpPw=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -785,6 +790,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
+github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
 github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
 github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY=
@@ -819,6 +825,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
@@ -849,8 +856,10 @@ github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
 github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
+github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
 github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw=
 github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
 github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@@ -939,6 +948,7 @@ github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z
 github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -991,6 +1001,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/pkcs11 v0.0.0-20190322140431-074fd7a1ed19/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo=
+github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
 github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
@@ -1131,6 +1142,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
 github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
 github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
@@ -1271,6 +1283,7 @@ github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34c
 github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
@@ -1282,6 +1295,7 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
 github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -1293,6 +1307,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
+github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
 github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@@ -1312,6 +1327,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
@@ -1321,6 +1337,7 @@ github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0
 github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
 github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
 github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
+github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz+cjWoom6l0=
 github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
@@ -1367,6 +1384,7 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
 github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
 github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
 github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
+github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg2K1HrLU=
 github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
 github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM=
 github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
@@ -1402,7 +1420,9 @@ github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
 github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
 github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
 github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
+github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e h1:mvOa4+/DXStR4ZXOks/UsjeFdn5O5JpLUtzqk9U8xXw=
 github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e/go.mod h1:w7kd3qXHh8FNaczNjslXqvFQiv5mMWRXlL9klTUAHc8=
+github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb h1:vxqkjztXSaPVDc8FQCdHTaejm2x747f6yPbnu1h2xkg=
 github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb/go.mod h1:29UiAJNsiVdvTBFCJW8e3q6dcDbOoPkhMgttOSCIMMY=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -1884,13 +1904,16 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/dancannon/gorethink.v3 v3.0.5 h1:/g7PWP7zUS6vSNmHSDbjCHQh1Rqn8Jy6zSMQxAsBSMQ=
 gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fatih/pool.v2 v2.0.0 h1:xIFeWtxifuQJGk/IEPKsTduEKcKvPmhoiVDGpC40nKg=
 gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/gorethink/gorethink.v3 v3.0.5 h1:e2Uc/Xe+hpcVQFsj6MuHlYog3r0JYpnTzwDj/y2O4MU=
 gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I=
 gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw=
 gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
diff --git a/kube/compose.go b/kube/compose.go
index 230914a6..ae83da30 100644
--- a/kube/compose.go
+++ b/kube/compose.go
@@ -119,7 +119,7 @@ func (s *composeService) Down(ctx context.Context, projectName string, options c
 }
 
 // List executes the equivalent to a `docker stack ls`
-func (s *composeService) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
+func (s *composeService) List(ctx context.Context) ([]compose.Stack, error) {
 	return s.sdk.ListReleases()
 }
 
diff --git a/local/compose/build.go b/local/compose/build.go
index 78f5426a..80efdbe7 100644
--- a/local/compose/build.go
+++ b/local/compose/build.go
@@ -34,18 +34,26 @@ import (
 
 func (s *composeService) Build(ctx context.Context, project *types.Project) error {
 	opts := map[string]build.Options{}
+	imagesToBuild := []string{}
 	for _, service := range project.Services {
 		if service.Build != nil {
 			imageName := getImageName(service, project.Name)
+			imagesToBuild = append(imagesToBuild, imageName)
 			opts[imageName] = s.toBuildOptions(service, project.WorkingDir, imageName)
 		}
 	}
 
-	return s.build(ctx, project, opts)
+	err := s.build(ctx, project, opts)
+	if err == nil {
+		displayScanSuggestMsg(ctx, imagesToBuild)
+	}
+
+	return err
 }
 
 func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project) error {
 	opts := map[string]build.Options{}
+	imagesToBuild := []string{}
 	for _, service := range project.Services {
 		if service.Image == "" && service.Build == nil {
 			return fmt.Errorf("invalid service %q. Must specify either image or build", service.Name)
@@ -66,6 +74,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
 			if localImagePresent && service.PullPolicy != types.PullPolicyBuild {
 				continue
 			}
+			imagesToBuild = append(imagesToBuild, imageName)
 			opts[imageName] = s.toBuildOptions(service, project.WorkingDir, imageName)
 			continue
 		}
@@ -84,7 +93,11 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
 
 	}
 
-	return s.build(ctx, project, opts)
+	err := s.build(ctx, project, opts)
+	if err == nil {
+		displayScanSuggestMsg(ctx, imagesToBuild)
+	}
+	return err
 }
 
 func (s *composeService) localImagePresent(ctx context.Context, imageName string) (bool, error) {
diff --git a/local/compose/ls.go b/local/compose/ls.go
index 64a4eb6f..2df40753 100644
--- a/local/compose/ls.go
+++ b/local/compose/ls.go
@@ -27,10 +27,9 @@ import (
 	"github.com/docker/docker/api/types/filters"
 )
 
-func (s *composeService) List(ctx context.Context, opts compose.ListOptions) ([]compose.Stack, error) {
+func (s *composeService) List(ctx context.Context) ([]compose.Stack, error) {
 	list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
 		Filters: filters.NewArgs(hasProjectLabelFilter()),
-		All:     opts.All,
 	})
 	if err != nil {
 		return nil, err
diff --git a/local/compose/scan_suggest.go b/local/compose/scan_suggest.go
new file mode 100644
index 00000000..2d5ab0fe
--- /dev/null
+++ b/local/compose/scan_suggest.go
@@ -0,0 +1,85 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	pluginmanager "github.com/docker/cli/cli-plugins/manager"
+	"github.com/docker/cli/cli/command"
+	"github.com/docker/compose-cli/api/config"
+)
+
+func displayScanSuggestMsg(ctx context.Context, builtImages []string) {
+	if len(builtImages) <= 0 {
+		return
+	}
+	if os.Getenv("DOCKER_SCAN_SUGGEST") == "false" {
+		return
+	}
+	if !scanAvailable() || scanAlreadyInvoked(ctx) {
+		return
+	}
+	fmt.Println("Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them")
+}
+
+func scanAlreadyInvoked(ctx context.Context) bool {
+	configDir := config.Dir(ctx)
+	filename := filepath.Join(configDir, "scan", "config.json")
+	f, err := os.Stat(filename)
+	if os.IsNotExist(err) {
+		return false
+	}
+	if f.IsDir() { // should never happen, do not bother user with suggestion if something goes wrong
+		return true
+	}
+	type scanOptin struct {
+		Optin bool `json:"optin"`
+	}
+	data, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return true
+	}
+	scanConfig := scanOptin{}
+	err = json.Unmarshal(data, &scanConfig)
+	if err != nil {
+		return true
+	}
+	return scanConfig.Optin
+}
+
+func scanAvailable() bool {
+	cli, err := command.NewDockerCli()
+	if err != nil {
+		return false
+	}
+	plugins, err := pluginmanager.ListPlugins(cli, nil)
+	if err != nil {
+		return false
+	}
+	for _, plugin := range plugins {
+		if plugin.Name == "scan" {
+			return true
+		}
+	}
+	return false
+}


Test patch:
diff --git a/aci/backend_test.go b/aci/backend_test.go
index 90917165..96432d90 100644
--- a/aci/backend_test.go
+++ b/aci/backend_test.go
@@ -23,9 +23,7 @@ import (
 	"github.com/stretchr/testify/mock"
 	"gotest.tools/v3/assert"
 
-	"github.com/docker/compose-cli/aci/login"
 	"github.com/docker/compose-cli/api/containers"
-	"golang.org/x/oauth2"
 )
 
 func TestGetContainerName(t *testing.T) {
@@ -84,7 +82,7 @@ func TestLoginParamsValidate(t *testing.T) {
 
 func TestLoginServicePrincipal(t *testing.T) {
 	loginService := mockLoginService{}
-	loginService.On("LoginServicePrincipal", "someID", "secret", "tenant", "someCloud").Return(nil)
+	loginService.On("LoginServicePrincipal", "someID", "secret", "tenant").Return(nil)
 	loginBackend := aciCloudService{
 		loginService: &loginService,
 	}
@@ -93,7 +91,6 @@ func TestLoginServicePrincipal(t *testing.T) {
 		ClientID:     "someID",
 		ClientSecret: "secret",
 		TenantID:     "tenant",
-		CloudName:    "someCloud",
 	})
 
 	assert.NilError(t, err)
@@ -102,14 +99,13 @@ func TestLoginServicePrincipal(t *testing.T) {
 func TestLoginWithTenant(t *testing.T) {
 	loginService := mockLoginService{}
 	ctx := context.Background()
-	loginService.On("Login", ctx, "tenant", "someCloud").Return(nil)
+	loginService.On("Login", ctx, "tenant").Return(nil)
 	loginBackend := aciCloudService{
 		loginService: &loginService,
 	}
 
 	err := loginBackend.Login(ctx, LoginParams{
-		TenantID:  "tenant",
-		CloudName: "someCloud",
+		TenantID: "tenant",
 	})
 
 	assert.NilError(t, err)
@@ -118,14 +114,12 @@ func TestLoginWithTenant(t *testing.T) {
 func TestLoginWithoutTenant(t *testing.T) {
 	loginService := mockLoginService{}
 	ctx := context.Background()
-	loginService.On("Login", ctx, "", "someCloud").Return(nil)
+	loginService.On("Login", ctx, "").Return(nil)
 	loginBackend := aciCloudService{
 		loginService: &loginService,
 	}
 
-	err := loginBackend.Login(ctx, LoginParams{
-		CloudName: "someCloud",
-	})
+	err := loginBackend.Login(ctx, LoginParams{})
 
 	assert.NilError(t, err)
 }
@@ -134,13 +128,13 @@ type mockLoginService struct {
 	mock.Mock
 }
 
-func (s *mockLoginService) Login(ctx context.Context, requestedTenantID string, cloudEnvironment string) error {
-	args := s.Called(ctx, requestedTenantID, cloudEnvironment)
+func (s *mockLoginService) Login(ctx context.Context, requestedTenantID string) error {
+	args := s.Called(ctx, requestedTenantID)
 	return args.Error(0)
 }
 
-func (s *mockLoginService) LoginServicePrincipal(clientID string, clientSecret string, tenantID string, cloudEnvironment string) error {
-	args := s.Called(clientID, clientSecret, tenantID, cloudEnvironment)
+func (s *mockLoginService) LoginServicePrincipal(clientID string, clientSecret string, tenantID string) error {
+	args := s.Called(clientID, clientSecret, tenantID)
 	return args.Error(0)
 }
 
@@ -148,18 +142,3 @@ func (s *mockLoginService) Logout(ctx context.Context) error {
 	args := s.Called(ctx)
 	return args.Error(0)
 }
-
-func (s *mockLoginService) GetTenantID() (string, error) {
-	args := s.Called()
-	return args.String(0), args.Error(1)
-}
-
-func (s *mockLoginService) GetCloudEnvironment() (login.CloudEnvironment, error) {
-	args := s.Called()
-	return args.Get(0).(login.CloudEnvironment), args.Error(1)
-}
-
-func (s *mockLoginService) GetValidToken() (oauth2.Token, string, error) {
-	args := s.Called()
-	return args.Get(0).(oauth2.Token), args.String(1), args.Error(2)
-}
diff --git a/aci/convert/registry_credentials_test.go b/aci/convert/registry_credentials_test.go
index 78ca7556..89077663 100644
--- a/aci/convert/registry_credentials_test.go
+++ b/aci/convert/registry_credentials_test.go
@@ -25,7 +25,6 @@ import (
 	"github.com/Azure/go-autorest/autorest/to"
 	"github.com/compose-spec/compose-go/types"
 	cliconfigtypes "github.com/docker/cli/cli/config/types"
-	"github.com/docker/compose-cli/aci/login"
 	"github.com/stretchr/testify/mock"
 	"gotest.tools/v3/assert"
 	is "gotest.tools/v3/assert/cmp"
@@ -256,7 +255,7 @@ func (s *MockRegistryHelper) getAllRegistryCredentials() (map[string]cliconfigty
 	return args.Get(0).(map[string]cliconfigtypes.AuthConfig), args.Error(1)
 }
 
-func (s *MockRegistryHelper) autoLoginAcr(registry string, loginService login.AzureLoginService) error {
-	args := s.Called(registry, loginService)
+func (s *MockRegistryHelper) autoLoginAcr(registry string) error {
+	args := s.Called(registry)
 	return args.Error(0)
 }
diff --git a/aci/e2e/e2e-aci_test.go b/aci/e2e/e2e-aci_test.go
index 87c0ce09..e28ed8ac 100644
--- a/aci/e2e/e2e-aci_test.go
+++ b/aci/e2e/e2e-aci_test.go
@@ -486,7 +486,7 @@ func TestContainerRunAttached(t *testing.T) {
 				c.RunDockerCmd("rm", "-f", container)
 			}
 			c.RunDockerCmd("run",
-				"--name", "fallback", // don't reuse the container name, this container is in a weird state and blocks everything
+				"--name", container,
 				"--memory", "0.1G", "--cpus", "0.1",
 				"nginx")
 		} else {
diff --git a/aci/login/client_test.go b/aci/login/client_test.go
deleted file mode 100644
index 4c77eec1..00000000
--- a/aci/login/client_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-   Copyright 2020 Docker Compose CLI authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package login
-
-import (
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"testing"
-
-	"gotest.tools/v3/assert"
-)
-
-func TestClearErrorMessageIfNotAlreadyLoggedIn(t *testing.T) {
-	dir, err := ioutil.TempDir("", "test_store")
-	assert.NilError(t, err)
-	t.Cleanup(func() {
-		_ = os.RemoveAll(dir)
-	})
-	_, _, err = getClientSetupDataImpl(filepath.Join(dir, tokenStoreFilename))
-	assert.ErrorContains(t, err, "not logged in to azure, you need to run \"docker login azure\" first")
-}
diff --git a/aci/login/cloud_environment_test.go b/aci/login/cloud_environment_test.go
deleted file mode 100644
index 57577761..00000000
--- a/aci/login/cloud_environment_test.go
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
-   Copyright 2020 Docker Compose CLI authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package login
-
-import (
-	"testing"
-
-	"gotest.tools/v3/assert"
-)
-
-func TestNormalizeCloudEnvironmentURLs(t *testing.T) {
-	ce := CloudEnvironment{
-		Name: "SecretCloud",
-		Authentication: CloudEnvironmentAuthentication{
-			LoginEndpoint: "https://login.here.com/",
-			Audiences: []string{
-				"https://audience1",
-				"https://audience2/",
-			},
-			Tenant: "common",
-		},
-		ResourceManagerURL: "invalid URL",
-	}
-
-	ce.normalizeURLs()
-
-	assert.Equal(t, ce.Authentication.LoginEndpoint, "https://login.here.com")
-	assert.Equal(t, ce.Authentication.Audiences[0], "https://audience1")
-	assert.Equal(t, ce.Authentication.Audiences[1], "https://audience2")
-}
-
-func TestApplyInvalidCloudMetadataJSON(t *testing.T) {
-	ce := newCloudEnvironmentService()
-	bb := []byte(`This isn't really valid JSON`)
-
-	err := ce.applyCloudMetadata(bb)
-
-	assert.Assert(t, err != nil, "Cloud metadata was invalid, so the application should have failed")
-	ensureWellKnownCloudMetadata(t, ce)
-}
-
-func TestApplyInvalidCloudMetatada(t *testing.T) {
-	ce := newCloudEnvironmentService()
-
-	// No name (moniker) for the cloud
-	bb := []byte(`
-	[{
-		"authentication": {
-			"loginEndpoint": "https://login.docker.com/",
-			"audiences": [
-				"https://management.docker.com/",
-				"https://management.cli.docker.com/"
-			],
-			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
-		},
-		"suffixes": {
-			"acrLoginServer": "azurecr.docker.io"
-		},
-		"resourceManager": "https://management.docker.com/"
-	}]`)
-
-	err := ce.applyCloudMetadata(bb)
-	assert.ErrorContains(t, err, "no name")
-	ensureWellKnownCloudMetadata(t, ce)
-
-	// Invalid resource manager URL
-	bb = []byte(`
-	[{
-		"authentication": {
-			"loginEndpoint": "https://login.docker.com/",
-			"audiences": [
-				"https://management.docker.com/",
-				"https://management.cli.docker.com/"
-			],
-			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
-		},
-		"name": "DockerAzureCloud",
-		"suffixes": {
-			"acrLoginServer": "azurecr.docker.io"
-		},
-		"resourceManager": "123"
-	}]`)
-
-	err = ce.applyCloudMetadata(bb)
-	assert.ErrorContains(t, err, "invalid resource manager URL")
-	ensureWellKnownCloudMetadata(t, ce)
-
-	// Invalid login endpoint
-	bb = []byte(`
-	[{
-		"authentication": {
-			"loginEndpoint": "456",
-			"audiences": [
-				"https://management.docker.com/",
-				"https://management.cli.docker.com/"
-			],
-			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
-		},
-		"name": "DockerAzureCloud",
-		"suffixes": {
-			"acrLoginServer": "azurecr.docker.io"
-		},
-		"resourceManager": "https://management.docker.com/"
-	}]`)
-
-	err = ce.applyCloudMetadata(bb)
-	assert.ErrorContains(t, err, "invalid login endpoint")
-	ensureWellKnownCloudMetadata(t, ce)
-
-	// No audiences
-	bb = []byte(`
-	[{
-		"authentication": {
-			"loginEndpoint": "https://login.docker.com/",
-			"audiences": [ ],
-			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
-		},
-		"name": "DockerAzureCloud",
-		"suffixes": {
-			"acrLoginServer": "azurecr.docker.io"
-		},
-		"resourceManager": "https://management.docker.com/"
-	}]`)
-
-	err = ce.applyCloudMetadata(bb)
-	assert.ErrorContains(t, err, "no authentication audiences")
-	ensureWellKnownCloudMetadata(t, ce)
-}
-
-func TestApplyCloudMetadata(t *testing.T) {
-	ce := newCloudEnvironmentService()
-
-	bb := []byte(`
-	[{
-		"authentication": {
-			"loginEndpoint": "https://login.docker.com/",
-			"audiences": [
-				"https://management.docker.com/",
-				"https://management.cli.docker.com/"
-			],
-			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
-		},
-		"name": "DockerAzureCloud",
-		"suffixes": {
-			"acrLoginServer": "azurecr.docker.io"
-		},
-		"resourceManager": "https://management.docker.com/"
-	}]`)
-
-	err := ce.applyCloudMetadata(bb)
-	assert.NilError(t, err)
-
-	env, err := ce.Get("DockerAzureCloud")
-	assert.NilError(t, err)
-	assert.Equal(t, env.Authentication.LoginEndpoint, "https://login.docker.com")
-	ensureWellKnownCloudMetadata(t, ce)
-}
-
-func TestDefaultCloudMetadataPresent(t *testing.T) {
-	ensureWellKnownCloudMetadata(t, CloudEnvironments)
-}
-
-func ensureWellKnownCloudMetadata(t *testing.T, ce CloudEnvironmentService) {
-	// Make sure well-known public cloud information is still available
-	_, err := ce.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	_, err = ce.Get("AzureChinaCloud")
-	assert.NilError(t, err)
-
-	_, err = ce.Get("AzureUSGovernment")
-	assert.NilError(t, err)
-}
diff --git a/aci/login/login_test.go b/aci/login/login_test.go
index c1d7a344..12330fe9 100644
--- a/aci/login/login_test.go
+++ b/aci/login/login_test.go
@@ -21,12 +21,10 @@ import (
 	"errors"
 	"io/ioutil"
 	"net/http"
-	"net/http/httptest"
 	"net/url"
 	"os"
 	"path/filepath"
 	"reflect"
-	"sync/atomic"
 	"testing"
 	"time"
 
@@ -38,7 +36,7 @@ import (
 	"golang.org/x/oauth2"
 )
 
-func testLoginService(t *testing.T, apiHelperMock *MockAzureHelper, cloudEnvironmentSvc CloudEnvironmentService) (*azureLoginService, error) {
+func testLoginService(t *testing.T, m *MockAzureHelper) (*AzureLoginService, error) {
 	dir, err := ioutil.TempDir("", "test_store")
 	if err != nil {
 		return nil, err
@@ -46,45 +44,20 @@ func testLoginService(t *testing.T, apiHelperMock *MockAzureHelper, cloudEnviron
 	t.Cleanup(func() {
 		_ = os.RemoveAll(dir)
 	})
-
-	ces := CloudEnvironments
-	if cloudEnvironmentSvc != nil {
-		ces = cloudEnvironmentSvc
-	}
-	return newAzureLoginServiceFromPath(filepath.Join(dir, tokenStoreFilename), apiHelperMock, ces)
+	return newAzureLoginServiceFromPath(filepath.Join(dir, tokenStoreFilename), m)
 }
 
 func TestRefreshInValidToken(t *testing.T) {
-	data := url.Values{
-		"grant_type":    []string{"refresh_token"},
-		"client_id":     []string{clientID},
-		"scope":         []string{"offline_access https://management.docker.com/.default"},
-		"refresh_token": []string{"refreshToken"},
-	}
-	helperMock := &MockAzureHelper{}
-	helperMock.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "123456").Return(azureToken{
+	data := refreshTokenData("refreshToken")
+	m := &MockAzureHelper{}
+	m.On("queryToken", data, "123456").Return(azureToken{
 		RefreshToken: "newRefreshToken",
 		AccessToken:  "newAccessToken",
 		ExpiresIn:    3600,
 		Foci:         "1",
 	}, nil)
 
-	cloudEnvironmentSvcMock := &MockCloudEnvironmentService{}
-	cloudEnvironmentSvcMock.On("Get", "AzureDockerCloud").Return(CloudEnvironment{
-		Name: "AzureDockerCloud",
-		Authentication: CloudEnvironmentAuthentication{
-			LoginEndpoint: "https://login.docker.com",
-			Audiences: []string{
-				"https://management.docker.com",
-				"https://management-ext.docker.com",
-			},
-			Tenant: "common",
-		},
-		ResourceManagerURL: "https://management.docker.com",
-		Suffixes:           map[string]string{},
-	}, nil)
-
-	azureLogin, err := testLoginService(t, helperMock, cloudEnvironmentSvcMock)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 	err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
 		TenantID: "123456",
@@ -94,51 +67,33 @@ func TestRefreshInValidToken(t *testing.T) {
 			Expiry:       time.Now().Add(-1 * time.Hour),
 			TokenType:    "Bearer",
 		},
-		CloudEnvironment: "AzureDockerCloud",
 	})
 	assert.NilError(t, err)
 
-	token, tenantID, err := azureLogin.GetValidToken()
-	assert.NilError(t, err)
-	assert.Equal(t, tenantID, "123456")
+	token, _ := azureLogin.GetValidToken()
 
 	assert.Equal(t, token.AccessToken, "newAccessToken")
 	assert.Assert(t, time.Now().Add(3500*time.Second).Before(token.Expiry))
 
-	storedToken, err := azureLogin.tokenStore.readToken()
-	assert.NilError(t, err)
+	storedToken, _ := azureLogin.tokenStore.readToken()
 	assert.Equal(t, storedToken.Token.AccessToken, "newAccessToken")
 	assert.Equal(t, storedToken.Token.RefreshToken, "newRefreshToken")
 	assert.Assert(t, time.Now().Add(3500*time.Second).Before(storedToken.Token.Expiry))
-
-	assert.Equal(t, storedToken.CloudEnvironment, "AzureDockerCloud")
 }
 
-func TestDoesNotRefreshValidToken(t *testing.T) {
-	expiryDate := time.Now().Add(1 * time.Hour)
-	azureLogin, err := testLoginService(t, nil, nil)
+func TestClearErrorMessageIfNotAlreadyLoggedIn(t *testing.T) {
+	dir, err := ioutil.TempDir("", "test_store")
 	assert.NilError(t, err)
-	err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
-		TenantID: "123456",
-		Token: oauth2.Token{
-			AccessToken:  "accessToken",
-			RefreshToken: "refreshToken",
-			Expiry:       expiryDate,
-			TokenType:    "Bearer",
-		},
-		CloudEnvironment: AzurePublicCloudName,
+	t.Cleanup(func() {
+		_ = os.RemoveAll(dir)
 	})
-	assert.NilError(t, err)
-
-	token, tenantID, err := azureLogin.GetValidToken()
-	assert.NilError(t, err)
-	assert.Equal(t, token.AccessToken, "accessToken")
-	assert.Equal(t, tenantID, "123456")
+	_, err = newAuthorizerFromLoginStorePath(filepath.Join(dir, tokenStoreFilename))
+	assert.ErrorContains(t, err, "not logged in to azure, you need to run \"docker login azure\" first")
 }
 
-func TestTokenStoreAssumesAzurePublicCloud(t *testing.T) {
+func TestDoesNotRefreshValidToken(t *testing.T) {
 	expiryDate := time.Now().Add(1 * time.Hour)
-	azureLogin, err := testLoginService(t, nil, nil)
+	azureLogin, err := testLoginService(t, nil)
 	assert.NilError(t, err)
 	err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
 		TenantID: "123456",
@@ -148,33 +103,25 @@ func TestTokenStoreAssumesAzurePublicCloud(t *testing.T) {
 			Expiry:       expiryDate,
 			TokenType:    "Bearer",
 		},
-		// Simulates upgrade from older version of Docker CLI that did not have cloud environment concept
-		CloudEnvironment: "",
 	})
 	assert.NilError(t, err)
 
-	token, tenantID, err := azureLogin.GetValidToken()
-	assert.NilError(t, err)
-	assert.Equal(t, tenantID, "123456")
+	token, _ := azureLogin.GetValidToken()
 	assert.Equal(t, token.AccessToken, "accessToken")
-
-	ce, err := azureLogin.GetCloudEnvironment()
-	assert.NilError(t, err)
-	assert.Equal(t, ce.Name, AzurePublicCloudName)
 }
 
 func TestInvalidLogin(t *testing.T) {
 	m := &MockAzureHelper{}
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 		redirectURL := args.Get(0).(string)
 		err := queryKeyValue(redirectURL, "error", "access denied: login failed")
 		assert.NilError(t, err)
 	}).Return(nil)
 
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(context.TODO(), "", AzurePublicCloudName)
+	err = azureLogin.Login(context.TODO(), "")
 	assert.Error(t, err, "no login code: login failed")
 }
 
@@ -182,22 +129,19 @@ func TestValidLogin(t *testing.T) {
 	var redirectURL string
 	ctx := context.TODO()
 	m := &MockAzureHelper{}
-	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 		redirectURL = args.Get(0).(string)
 		err := queryKeyValue(redirectURL, "code", "123456879")
 		assert.NilError(t, err)
 	}).Return(nil)
 
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
+	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 		return reflect.DeepEqual(data, url.Values{
 			"grant_type":   []string{"authorization_code"},
 			"client_id":    []string{clientID},
 			"code":         []string{"123456879"},
-			"scope":        []string{ce.GetTokenScope()},
+			"scope":        []string{scopes},
 			"redirect_uri": []string{redirectURL},
 		})
 	}), "organizations").Return(azureToken{
@@ -209,18 +153,18 @@ func TestValidLogin(t *testing.T) {
 
 	authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 
-	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
-	data := refreshTokenData("firstRefreshToken", ce)
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
+	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
+	data := refreshTokenData("firstRefreshToken")
+	m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 		RefreshToken: "newRefreshToken",
 		AccessToken:  "newAccessToken",
 		ExpiresIn:    3600,
 		Foci:         "1",
 	}, nil)
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
+	err = azureLogin.Login(ctx, "")
 	assert.NilError(t, err)
 
 	loginToken, err := azureLogin.tokenStore.readToken()
@@ -230,28 +174,24 @@ func TestValidLogin(t *testing.T) {
 	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 	assert.Equal(t, loginToken.TenantID, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 	assert.Equal(t, loginToken.Token.Type(), "Bearer")
-	assert.Equal(t, loginToken.CloudEnvironment, "AzureCloud")
 }
 
 func TestValidLoginRequestedTenant(t *testing.T) {
 	var redirectURL string
 	m := &MockAzureHelper{}
-	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 		redirectURL = args.Get(0).(string)
 		err := queryKeyValue(redirectURL, "code", "123456879")
 		assert.NilError(t, err)
 	}).Return(nil)
 
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
+	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 		return reflect.DeepEqual(data, url.Values{
 			"grant_type":   []string{"authorization_code"},
 			"client_id":    []string{clientID},
 			"code":         []string{"123456879"},
-			"scope":        []string{ce.GetTokenScope()},
+			"scope":        []string{scopes},
 			"redirect_uri": []string{redirectURL},
 		})
 	}), "organizations").Return(azureToken{
@@ -265,18 +205,18 @@ func TestValidLoginRequestedTenant(t *testing.T) {
 						   {"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 
 	ctx := context.TODO()
-	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
-	data := refreshTokenData("firstRefreshToken", ce)
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
+	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
+	data := refreshTokenData("firstRefreshToken")
+	m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 		RefreshToken: "newRefreshToken",
 		AccessToken:  "newAccessToken",
 		ExpiresIn:    3600,
 		Foci:         "1",
 	}, nil)
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(ctx, "12345a7c-c56d-43e8-9549-dd230ce8a038", AzurePublicCloudName)
+	err = azureLogin.Login(ctx, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 	assert.NilError(t, err)
 
 	loginToken, err := azureLogin.tokenStore.readToken()
@@ -286,28 +226,24 @@ func TestValidLoginRequestedTenant(t *testing.T) {
 	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 	assert.Equal(t, loginToken.TenantID, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 	assert.Equal(t, loginToken.Token.Type(), "Bearer")
-	assert.Equal(t, loginToken.CloudEnvironment, "AzureCloud")
 }
 
 func TestLoginNoTenant(t *testing.T) {
 	var redirectURL string
 	m := &MockAzureHelper{}
-	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 		redirectURL = args.Get(0).(string)
 		err := queryKeyValue(redirectURL, "code", "123456879")
 		assert.NilError(t, err)
 	}).Return(nil)
 
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
+	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 		return reflect.DeepEqual(data, url.Values{
 			"grant_type":   []string{"authorization_code"},
 			"client_id":    []string{clientID},
 			"code":         []string{"123456879"},
-			"scope":        []string{ce.GetTokenScope()},
+			"scope":        []string{scopes},
 			"redirect_uri": []string{redirectURL},
 		})
 	}), "organizations").Return(azureToken{
@@ -319,34 +255,31 @@ func TestLoginNoTenant(t *testing.T) {
 
 	ctx := context.TODO()
 	authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
-	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
+	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(ctx, "00000000-c56d-43e8-9549-dd230ce8a038", AzurePublicCloudName)
+	err = azureLogin.Login(ctx, "00000000-c56d-43e8-9549-dd230ce8a038")
 	assert.Error(t, err, "could not find requested azure tenant 00000000-c56d-43e8-9549-dd230ce8a038: login failed")
 }
 
 func TestLoginRequestedTenantNotFound(t *testing.T) {
 	var redirectURL string
 	m := &MockAzureHelper{}
-	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 		redirectURL = args.Get(0).(string)
 		err := queryKeyValue(redirectURL, "code", "123456879")
 		assert.NilError(t, err)
 	}).Return(nil)
 
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
+	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 		return reflect.DeepEqual(data, url.Values{
 			"grant_type":   []string{"authorization_code"},
 			"client_id":    []string{clientID},
 			"code":         []string{"123456879"},
-			"scope":        []string{ce.GetTokenScope()},
+			"scope":        []string{scopes},
 			"redirect_uri": []string{redirectURL},
 		})
 	}), "organizations").Return(azureToken{
@@ -358,34 +291,31 @@ func TestLoginRequestedTenantNotFound(t *testing.T) {
 
 	ctx := context.TODO()
 	authBody := `{"value":[]}`
-	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
+	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
+	err = azureLogin.Login(ctx, "")
 	assert.Error(t, err, "could not find azure tenant: login failed")
 }
 
 func TestLoginAuthorizationFailed(t *testing.T) {
 	var redirectURL string
 	m := &MockAzureHelper{}
-	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 		redirectURL = args.Get(0).(string)
 		err := queryKeyValue(redirectURL, "code", "123456879")
 		assert.NilError(t, err)
 	}).Return(nil)
 
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
+	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 		return reflect.DeepEqual(data, url.Values{
 			"grant_type":   []string{"authorization_code"},
 			"client_id":    []string{clientID},
 			"code":         []string{"123456879"},
-			"scope":        []string{ce.GetTokenScope()},
+			"scope":        []string{scopes},
 			"redirect_uri": []string{redirectURL},
 		})
 	}), "organizations").Return(azureToken{
@@ -398,38 +328,35 @@ func TestLoginAuthorizationFailed(t *testing.T) {
 	authBody := `[access denied]`
 
 	ctx := context.TODO()
-	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 400, nil)
+	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 400, nil)
 
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
+	err = azureLogin.Login(ctx, "")
 	assert.Error(t, err, "unable to login status code 400: [access denied]: login failed")
 }
 
 func TestValidThroughDeviceCodeFlow(t *testing.T) {
 	m := &MockAzureHelper{}
-	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
-	assert.NilError(t, err)
-
-	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Return(errors.New("Could not open browser"))
-	m.On("getDeviceCodeFlowToken", mock.AnythingOfType("CloudEnvironment")).Return(adal.Token{AccessToken: "firstAccessToken", RefreshToken: "firstRefreshToken"}, nil)
+	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Return(errors.New("Could not open browser"))
+	m.On("getDeviceCodeFlowToken").Return(adal.Token{AccessToken: "firstAccessToken", RefreshToken: "firstRefreshToken"}, nil)
 
 	authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 
 	ctx := context.TODO()
-	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
-	data := refreshTokenData("firstRefreshToken", ce)
-	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
+	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
+	data := refreshTokenData("firstRefreshToken")
+	m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 		RefreshToken: "newRefreshToken",
 		AccessToken:  "newAccessToken",
 		ExpiresIn:    3600,
 		Foci:         "1",
 	}, nil)
-	azureLogin, err := testLoginService(t, m, nil)
+	azureLogin, err := testLoginService(t, m)
 	assert.NilError(t, err)
 
-	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
+	err = azureLogin.Login(ctx, "")
 	assert.NilError(t, err)
 
 	loginToken, err := azureLogin.tokenStore.readToken()
@@ -439,110 +366,13 @@ func TestValidThroughDeviceCodeFlow(t *testing.T) {
 	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 	assert.Equal(t, loginToken.TenantID, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 	assert.Equal(t, loginToken.Token.Type(), "Bearer")
-	assert.Equal(t, loginToken.CloudEnvironment, "AzureCloud")
 }
 
-func TestNonstandardCloudEnvironment(t *testing.T) {
-	dockerCloudMetadata := []byte(`
-	[{
-		"authentication": {
-			"loginEndpoint": "https://login.docker.com/",
-			"audiences": [
-				"https://management.docker.com/",
-				"https://management.cli.docker.com/"
-			],
-			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
-		},
-		"name": "AzureDockerCloud",
-		"suffixes": {
-			"acrLoginServer": "azurecr.docker.io"
-		},
-		"resourceManager": "https://management.docker.com/"
-	}]`)
-	var metadataReqCount int32 = 0
-	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		_, err := w.Write(dockerCloudMetadata)
-		assert.NilError(t, err)
-		atomic.AddInt32(&metadataReqCount, 1)
-	}))
-	defer srv.Close()
-
-	cloudMetadataURL, cloudMetadataURLSet := os.LookupEnv(CloudMetadataURLVar)
-	if cloudMetadataURLSet {
-		defer func() {
-			err := os.Setenv(CloudMetadataURLVar, cloudMetadataURL)
-			assert.NilError(t, err)
-		}()
-	}
-	err := os.Setenv(CloudMetadataURLVar, srv.URL)
-	assert.NilError(t, err)
-
-	ctx := context.TODO()
-
-	ces := newCloudEnvironmentService()
-	ces.cloudMetadataURL = srv.URL
-	dockerCloudEnv, err := ces.Get("AzureDockerCloud")
-	assert.NilError(t, err)
-
-	helperMock := &MockAzureHelper{}
-	var redirectURL string
-	helperMock.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
-		redirectURL = args.Get(0).(string)
-		err := queryKeyValue(redirectURL, "code", "123456879")
-		assert.NilError(t, err)
-	}).Return(nil)
-
-	helperMock.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
-		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
-		return reflect.DeepEqual(data, url.Values{
-			"grant_type":   []string{"authorization_code"},
-			"client_id":    []string{clientID},
-			"code":         []string{"123456879"},
-			"scope":        []string{dockerCloudEnv.GetTokenScope()},
-			"redirect_uri": []string{redirectURL},
-		})
-	}), "organizations").Return(azureToken{
-		RefreshToken: "firstRefreshToken",
-		AccessToken:  "firstAccessToken",
-		ExpiresIn:    3600,
-		Foci:         "1",
-	}, nil)
-
-	authBody := `{"value":[{"id":"/tenants/F5773994-FE88-482E-9E33-6E799D250416","tenantId":"F5773994-FE88-482E-9E33-6E799D250416"}]}`
-
-	helperMock.On("queryAPIWithHeader", ctx, dockerCloudEnv.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
-	data := refreshTokenData("firstRefreshToken", dockerCloudEnv)
-	helperMock.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "F5773994-FE88-482E-9E33-6E799D250416").Return(azureToken{
-		RefreshToken: "newRefreshToken",
-		AccessToken:  "newAccessToken",
-		ExpiresIn:    3600,
-		Foci:         "1",
-	}, nil)
-
-	azureLogin, err := testLoginService(t, helperMock, ces)
-	assert.NilError(t, err)
-
-	err = azureLogin.Login(ctx, "", "AzureDockerCloud")
-	assert.NilError(t, err)
-
-	loginToken, err := azureLogin.tokenStore.readToken()
-	assert.NilError(t, err)
-	assert.Equal(t, loginToken.Token.AccessToken, "newAccessToken")
-	assert.Equal(t, loginToken.Token.RefreshToken, "newRefreshToken")
-	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
-	assert.Equal(t, loginToken.TenantID, "F5773994-FE88-482E-9E33-6E799D250416")
-	assert.Equal(t, loginToken.Token.Type(), "Bearer")
-	assert.Equal(t, loginToken.CloudEnvironment, "AzureDockerCloud")
-	assert.Equal(t, metadataReqCount, int32(1))
-}
-
-// Don't warn about refreshToken parameter taking the same value for all invocations
-// nolint:unparam
-func refreshTokenData(refreshToken string, ce CloudEnvironment) url.Values {
+func refreshTokenData(refreshToken string) url.Values {
 	return url.Values{
 		"grant_type":    []string{"refresh_token"},
 		"client_id":     []string{clientID},
-		"scope":         []string{ce.GetTokenScope()},
+		"scope":         []string{scopes},
 		"refresh_token": []string{refreshToken},
 	}
 }
@@ -564,13 +394,13 @@ type MockAzureHelper struct {
 	mock.Mock
 }
 
-func (s *MockAzureHelper) getDeviceCodeFlowToken(ce CloudEnvironment) (adal.Token, error) {
-	args := s.Called(ce)
+func (s *MockAzureHelper) getDeviceCodeFlowToken() (adal.Token, error) {
+	args := s.Called()
 	return args.Get(0).(adal.Token), args.Error(1)
 }
 
-func (s *MockAzureHelper) queryToken(ce CloudEnvironment, data url.Values, tenantID string) (token azureToken, err error) {
-	args := s.Called(ce, data, tenantID)
+func (s *MockAzureHelper) queryToken(data url.Values, tenantID string) (token azureToken, err error) {
+	args := s.Called(data, tenantID)
 	return args.Get(0).(azureToken), args.Error(1)
 }
 
@@ -579,16 +409,7 @@ func (s *MockAzureHelper) queryAPIWithHeader(ctx context.Context, authorizationU
 	return args.Get(0).([]byte), args.Int(1), args.Error(2)
 }
 
-func (s *MockAzureHelper) openAzureLoginPage(redirectURL string, ce CloudEnvironment) error {
-	args := s.Called(redirectURL, ce)
+func (s *MockAzureHelper) openAzureLoginPage(redirectURL string) error {
+	args := s.Called(redirectURL)
 	return args.Error(0)
 }
-
-type MockCloudEnvironmentService struct {
-	mock.Mock
-}
-
-func (s *MockCloudEnvironmentService) Get(name string) (CloudEnvironment, error) {
-	args := s.Called(name)
-	return args.Get(0).(CloudEnvironment), args.Error(1)
-}
diff --git a/local/e2e/compose/fixtures/start-stop/compose.yml b/local/e2e/compose/fixtures/start-stop/compose.yml
deleted file mode 100644
index 7ee4c157..00000000
--- a/local/e2e/compose/fixtures/start-stop/compose.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-services:
-  simple:
-    image:  nginx
-  another:
-    image:  nginx
diff --git a/local/e2e/compose/start_stop_test.go b/local/e2e/compose/start_stop_test.go
deleted file mode 100644
index 194288a0..00000000
--- a/local/e2e/compose/start_stop_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-   Copyright 2020 Docker Compose CLI authors
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-package e2e
-
-import (
-	"fmt"
-	"strings"
-	"testing"
-
-	testify "github.com/stretchr/testify/assert"
-	"gotest.tools/v3/assert"
-
-	. "github.com/docker/compose-cli/utils/e2e"
-)
-
-func TestStartStop(t *testing.T) {
-	c := NewParallelE2eCLI(t, binDir)
-	const projectName = "e2e-start-stop"
-
-	getProjectRegx := func(status string) string {
-		// match output with random spaces like:
-		// e2e-start-stop      running(3)
-		return fmt.Sprintf("%s\\s+%s\\(%d\\)", projectName, status, 2)
-	}
-
-	getServiceRegx := func(service string, status string) string {
-		// match output with random spaces like:
-		// e2e-start-stop_db_1      db                  running
-		return fmt.Sprintf("%s_%s_1\\s+%s\\s+%s", projectName, service, service, status)
-	}
-
-	t.Run("Up a project", func(t *testing.T) {
-		res := c.RunDockerOrExitError("compose", "-f", "./fixtures/start-stop/compose.yml", "--project-name", projectName, "up", "-d")
-		assert.Assert(t, strings.Contains("Container e2e-start-stop_simple_1  Started", res.Stdout()), res.Stdout())
-
-		res = c.RunDockerOrExitError("compose", "ls", "--all")
-		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
-
-		res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps")
-		testify.Regexp(t, getServiceRegx("simple", "running"), res.Stdout())
-		testify.Regexp(t, getServiceRegx("another", "running"), res.Stdout())
-	})
-
-	t.Run("stop project", func(t *testing.T) {
-		c.RunDockerOrExitError("compose", "-f", "./fixtures/start-stop/compose.yml", "--project-name", projectName, "stop")
-
-		res := c.RunDockerOrExitError("compose", "ls")
-		assert.Assert(t, !strings.Contains("e2e-start-stop", res.Combined()), res.Combined())
-
-		res = c.RunDockerOrExitError("compose", "ls", "--all")
-		testify.Regexp(t, getProjectRegx("exited"), res.Stdout())
-
-		res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps")
-		assert.Assert(t, !strings.Contains("e2e-start-stop_words_1", res.Combined()), res.Combined())
-
-		res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps", "--all")
-		testify.Regexp(t, getServiceRegx("simple", "exited"), res.Stdout())
-		testify.Regexp(t, getServiceRegx("another", "exited"), res.Stdout())
-	})
-
-	t.Run("start project", func(t *testing.T) {
-		c.RunDockerOrExitError("compose", "-f", "./fixtures/start-stop/compose.yml", "--project-name", projectName, "start")
-
-		res := c.RunDockerOrExitError("compose", "ls")
-		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
-	})
-
-	t.Run("pause project", func(t *testing.T) {
-		c.RunDockerOrExitError("compose", "-f", "./fixtures/start-stop/compose.yml", "--project-name", projectName, "pause")
-
-		res := c.RunDockerOrExitError("compose", "ls")
-		assert.Assert(t, !strings.Contains("e2e-start-stop", res.Combined()), res.Combined())
-
-		res = c.RunDockerOrExitError("compose", "ls", "--all")
-		testify.Regexp(t, getProjectRegx("paused"), res.Stdout())
-	})
-
-	t.Run("unpause project", func(t *testing.T) {
-		c.RunDockerOrExitError("compose", "-f", "./fixtures/start-stop/compose.yml", "--project-name", projectName, "unpause")
-
-		res := c.RunDockerOrExitError("compose", "ls")
-		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
-	})
-
-	t.Run("down", func(t *testing.T) {
-		_ = c.RunDockerCmd("compose", "--project-name", projectName, "down")
-	})
-}