2020-06-18 16:13:24 +02:00
/ *
2020-09-22 12:13:00 +02:00
Copyright 2020 Docker Compose CLI authors
2020-06-18 16:13:24 +02:00
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 .
* /
2020-05-05 17:55:53 +02:00
package main
import (
2020-11-12 15:16:30 +01:00
"bytes"
2020-05-05 17:55:53 +02:00
"context"
2020-11-12 15:16:30 +01:00
"encoding/json"
2020-08-06 15:40:36 +02:00
"errors"
2020-05-19 11:29:48 +02:00
"fmt"
2020-10-01 16:03:22 +02:00
"io/ioutil"
2020-10-02 18:16:13 +02:00
"math/rand"
2020-08-06 15:40:36 +02:00
"net/http"
2020-05-19 11:29:48 +02:00
"net/url"
2020-06-29 17:57:06 +02:00
"os"
2020-10-01 16:03:22 +02:00
"path/filepath"
2020-08-06 15:40:36 +02:00
"runtime"
"strconv"
2020-05-18 11:00:09 +02:00
"strings"
2020-08-06 15:40:36 +02:00
"syscall"
2020-05-20 15:57:10 +02:00
"testing"
2020-06-30 16:33:36 +02:00
"time"
2020-06-02 23:33:41 +02:00
2020-10-29 12:42:59 +01:00
"github.com/docker/docker/pkg/fileutils"
2020-08-06 15:40:36 +02:00
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/icmd"
"gotest.tools/v3/poll"
2020-07-08 16:01:54 +02:00
2020-05-05 17:55:53 +02:00
"github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources"
2020-05-19 11:29:48 +02:00
"github.com/Azure/azure-storage-file-go/azfile"
2020-05-20 18:05:32 +02:00
"github.com/Azure/go-autorest/autorest/to"
2020-10-06 17:46:13 +02:00
"github.com/prometheus/tsdb/fileutil"
2020-05-13 07:52:43 +02:00
2020-08-21 17:24:53 +02:00
"github.com/docker/compose-cli/aci"
2020-10-01 17:27:57 +02:00
"github.com/docker/compose-cli/aci/convert"
2020-08-21 17:24:53 +02:00
"github.com/docker/compose-cli/aci/login"
2020-09-07 13:22:08 +02:00
"github.com/docker/compose-cli/api/containers"
2021-01-15 16:31:59 +01:00
"github.com/docker/compose-cli/api/context/store"
2021-01-15 16:51:31 +01:00
"github.com/docker/compose-cli/api/errdefs"
2020-11-12 15:16:30 +01:00
"github.com/docker/compose-cli/cli/cmd"
2021-01-19 15:34:54 +01:00
. "github.com/docker/compose-cli/utils/e2e"
2020-05-05 17:55:53 +02:00
)
2020-05-13 08:54:48 +02:00
const (
2020-08-06 15:40:36 +02:00
contextName = "aci-test"
2020-05-13 08:54:48 +02:00
)
2020-05-05 17:55:53 +02:00
2020-10-02 18:16:13 +02:00
var (
2020-10-12 13:46:41 +02:00
binDir string
2020-10-06 13:00:55 +02:00
location = [ ] string { "eastus2" }
2020-10-02 18:16:13 +02:00
)
2020-05-20 15:57:10 +02:00
2020-08-06 15:40:36 +02:00
func TestMain ( m * testing . M ) {
p , cleanup , err := SetupExistingCLI ( )
if err != nil {
fmt . Println ( err )
os . Exit ( 1 )
}
binDir = p
exitCode := m . Run ( )
cleanup ( )
os . Exit ( exitCode )
2020-05-20 15:57:10 +02:00
}
2020-05-05 17:55:53 +02:00
2020-08-06 15:40:36 +02:00
// Cannot be parallelized as login/logout is global.
func TestLoginLogout ( t * testing . T ) {
startTime := strconv . Itoa ( int ( time . Now ( ) . UnixNano ( ) ) )
c := NewE2eCLI ( t , binDir )
rg := "E2E-" + startTime
t . Run ( "login" , func ( t * testing . T ) {
2020-08-10 16:38:59 +02:00
azureLogin ( t , c )
2020-08-06 15:40:36 +02:00
} )
t . Run ( "create context" , func ( t * testing . T ) {
sID := getSubscriptionID ( t )
2020-10-02 18:16:13 +02:00
location := getTestLocation ( )
err := createResourceGroup ( t , sID , rg , location )
2020-08-06 15:40:36 +02:00
assert . Check ( t , is . Nil ( err ) )
t . Cleanup ( func ( ) {
2020-08-28 14:38:51 +02:00
_ = deleteResourceGroup ( t , rg )
2020-08-06 15:40:36 +02:00
} )
2020-08-24 10:23:14 +02:00
c . RunDockerCmd ( "context" , "create" , "aci" , contextName , "--subscription-id" , sID , "--resource-group" , rg , "--location" , location )
res := c . RunDockerCmd ( "context" , "use" , contextName )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : contextName } )
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "context" , "ls" )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : contextName + " *" } )
} )
t . Run ( "delete context" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "context" , "use" , "default" )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : "default" } )
2020-07-08 16:01:54 +02:00
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "context" , "rm" , contextName )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : contextName } )
} )
t . Run ( "logout" , func ( t * testing . T ) {
2020-07-08 16:01:54 +02:00
_ , err := os . Stat ( login . GetTokenStorePath ( ) )
2020-08-06 15:40:36 +02:00
assert . NilError ( t , err )
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "logout" , "azure" )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : "Removing login credentials for Azure" } )
_ , err = os . Stat ( login . GetTokenStorePath ( ) )
2020-08-10 15:28:05 +02:00
errMsg := "no such file or directory"
if runtime . GOOS == "windows" {
errMsg = "The system cannot find the file specified"
}
assert . ErrorContains ( t , err , errMsg )
2020-07-08 16:01:54 +02:00
} )
2020-08-06 15:40:36 +02:00
t . Run ( "create context fail" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerOrExitError ( "context" , "create" , "aci" , "fail-context" )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected {
ExitCode : errdefs . ExitCodeLoginRequired ,
Err : ` not logged in to azure, you need to run "docker login azure" first ` ,
} )
2020-07-08 16:01:54 +02:00
} )
}
2020-10-02 18:16:13 +02:00
func getTestLocation ( ) string {
rand . Seed ( time . Now ( ) . Unix ( ) )
n := rand . Intn ( len ( location ) )
return location [ n ]
}
2020-10-01 16:03:22 +02:00
func uploadTestFile ( t * testing . T , aciContext store . AciContext , accountName string , fileshareName string , testFileName string , testFileContent string ) {
storageLogin := login . StorageLoginImpl { AciContext : aciContext }
key , err := storageLogin . GetAzureStorageAccountKey ( context . TODO ( ) , accountName )
assert . NilError ( t , err )
cred , err := azfile . NewSharedKeyCredential ( accountName , key )
assert . NilError ( t , err )
u , _ := url . Parse ( fmt . Sprintf ( "https://%s.file.core.windows.net/%s" , accountName , fileshareName ) )
uploadFile ( t , * cred , u . String ( ) , testFileName , testFileContent )
}
2020-10-13 14:10:48 +02:00
const (
fileshareName = "dockertestshare"
fileshareName2 = "dockertestshare2"
)
2020-08-06 15:40:36 +02:00
2020-10-01 16:03:22 +02:00
func TestRunVolume ( t * testing . T ) {
2020-08-06 15:40:36 +02:00
const (
testFileContent = "Volume mounted successfully!"
testFileName = "index.html"
)
2020-10-01 16:03:22 +02:00
c := NewParallelE2eCLI ( t , binDir )
sID , rg , location := setupTestResourceGroup ( t , c )
2020-08-06 15:40:36 +02:00
// Bootstrap volume
aciContext := store . AciContext {
SubscriptionID : sID ,
Location : location ,
ResourceGroup : rg ,
}
// Used in subtests
var (
2020-09-08 14:20:01 +02:00
container string
hostIP string
endpoint string
volumeID string
accountName = "e2e" + strconv . Itoa ( int ( time . Now ( ) . UnixNano ( ) ) )
2020-08-06 15:40:36 +02:00
)
2020-09-24 09:52:17 +02:00
t . Run ( "check empty volume name validity" , func ( t * testing . T ) {
invalidName := ""
2020-09-24 14:58:19 +02:00
res := c . RunDockerOrExitError ( "volume" , "create" , "--storage-account" , invalidName , fileshareName )
2020-09-24 09:52:17 +02:00
res . Assert ( t , icmd . Expected {
ExitCode : 1 ,
Err : ` parameter=accountName constraint=MinLength value="" details: value length must be greater than or equal to 3 ` ,
} )
} )
2020-09-08 15:27:56 +02:00
t . Run ( "check volume name validity" , func ( t * testing . T ) {
invalidName := "some-storage-123"
2020-09-24 14:58:19 +02:00
res := c . RunDockerOrExitError ( "volume" , "create" , "--storage-account" , invalidName , fileshareName )
2020-09-08 15:27:56 +02:00
res . Assert ( t , icmd . Expected {
ExitCode : 1 ,
Err : "some-storage-123 is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only." ,
} )
} )
t . Run ( "create volumes" , func ( t * testing . T ) {
2020-09-24 14:58:19 +02:00
c . RunDockerCmd ( "volume" , "create" , "--storage-account" , accountName , fileshareName )
2020-09-08 14:20:01 +02:00
} )
2020-10-01 16:03:22 +02:00
volumeID = accountName + "/" + fileshareName
2020-09-08 15:27:56 +02:00
t . Cleanup ( func ( ) {
2020-09-09 15:49:43 +02:00
c . RunDockerCmd ( "volume" , "rm" , volumeID )
2020-09-08 15:27:56 +02:00
res := c . RunDockerCmd ( "volume" , "ls" )
2020-11-27 16:15:13 +01:00
lines := Lines ( res . Stdout ( ) )
2020-09-08 15:27:56 +02:00
assert . Equal ( t , len ( lines ) , 1 )
} )
t . Run ( "create second fileshare" , func ( t * testing . T ) {
2020-10-13 14:10:48 +02:00
c . RunDockerCmd ( "volume" , "create" , "--storage-account" , accountName , fileshareName2 )
2020-09-08 14:20:01 +02:00
} )
2020-10-13 14:10:48 +02:00
volumeID2 := accountName + "/" + fileshareName2
2020-09-08 14:20:01 +02:00
t . Run ( "list volumes" , func ( t * testing . T ) {
2020-10-12 11:03:43 +02:00
res := c . RunDockerCmd ( "volume" , "ls" , "--quiet" )
2020-11-27 16:15:13 +01:00
l := Lines ( res . Stdout ( ) )
2020-10-12 11:03:43 +02:00
assert . Equal ( t , len ( l ) , 2 )
assert . Equal ( t , l [ 0 ] , volumeID )
assert . Equal ( t , l [ 1 ] , volumeID2 )
res = c . RunDockerCmd ( "volume" , "ls" )
2020-11-27 16:15:13 +01:00
l = Lines ( res . Stdout ( ) )
2020-10-12 11:03:43 +02:00
assert . Equal ( t , len ( l ) , 3 )
firstAccount := l [ 1 ]
2020-09-08 14:20:01 +02:00
fields := strings . Fields ( firstAccount )
assert . Equal ( t , fields [ 0 ] , volumeID )
2020-10-12 11:03:43 +02:00
secondAccount := l [ 2 ]
2020-09-08 14:20:01 +02:00
fields = strings . Fields ( secondAccount )
2020-09-09 15:49:43 +02:00
assert . Equal ( t , fields [ 0 ] , volumeID2 )
2020-09-08 14:20:01 +02:00
} )
2020-10-13 14:10:48 +02:00
t . Run ( "inspect volumes" , func ( t * testing . T ) {
res := c . RunDockerCmd ( "volume" , "inspect" , volumeID )
assert . Equal ( t , res . Stdout ( ) , fmt . Sprintf ( ` {
"ID" : % q ,
"Description" : "Fileshare %s in %s storage account"
}
` , volumeID , fileshareName , accountName ) )
res = c . RunDockerCmd ( "volume" , "inspect" , volumeID2 )
assert . Equal ( t , res . Stdout ( ) , fmt . Sprintf ( ` {
"ID" : % q ,
"Description" : "Fileshare %s in %s storage account"
}
` , volumeID2 , fileshareName2 , accountName ) )
} )
2020-09-08 15:27:56 +02:00
t . Run ( "delete only fileshare" , func ( t * testing . T ) {
2020-09-09 15:49:43 +02:00
c . RunDockerCmd ( "volume" , "rm" , volumeID2 )
2020-09-08 15:27:56 +02:00
res := c . RunDockerCmd ( "volume" , "ls" )
2020-11-27 16:15:13 +01:00
lines := Lines ( res . Stdout ( ) )
2020-09-08 15:27:56 +02:00
assert . Equal ( t , len ( lines ) , 2 )
2020-10-13 14:10:48 +02:00
assert . Assert ( t , ! strings . Contains ( res . Stdout ( ) , fileshareName2 ) , "second fileshare still visible after rm" )
2020-09-08 15:27:56 +02:00
} )
2020-09-08 14:20:01 +02:00
t . Run ( "upload file" , func ( t * testing . T ) {
2020-10-01 16:03:22 +02:00
uploadTestFile ( t , aciContext , accountName , fileshareName , testFileName , testFileContent )
2020-09-08 14:20:01 +02:00
} )
2020-08-06 15:40:36 +02:00
t . Run ( "run" , func ( t * testing . T ) {
2020-05-19 19:51:22 +02:00
mountTarget := "/usr/share/nginx/html"
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd (
2020-08-06 15:40:36 +02:00
"run" , "-d" ,
2020-09-08 14:20:01 +02:00
"-v" , fmt . Sprintf ( "%s:%s" , volumeID , mountTarget ) ,
2020-05-19 19:51:22 +02:00
"-p" , "80:80" ,
2020-08-06 15:40:36 +02:00
"nginx" ,
)
container = getContainerName ( res . Stdout ( ) )
2020-05-13 09:00:48 +02:00
} )
2020-08-06 15:40:36 +02:00
t . Run ( "inspect" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "inspect" , container )
2020-07-16 11:05:31 +02:00
2020-11-12 15:16:30 +01:00
containerInspect , err := parseContainerInspect ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
assert . NilError ( t , err )
assert . Equal ( t , containerInspect . Platform , "Linux" )
2020-10-07 18:25:01 +02:00
assert . Equal ( t , containerInspect . HostConfig . CPULimit , 1.0 )
2020-10-08 17:03:26 +02:00
assert . Equal ( t , containerInspect . HostConfig . CPUReservation , 1.0 )
2020-10-07 18:25:01 +02:00
assert . Equal ( t , containerInspect . HostConfig . RestartPolicy , containers . RestartPolicyNone )
2020-07-08 14:31:27 +02:00
2020-08-06 15:40:36 +02:00
assert . Assert ( t , is . Len ( containerInspect . Ports , 1 ) )
hostIP = containerInspect . Ports [ 0 ] . HostIP
endpoint = fmt . Sprintf ( "http://%s:%d" , containerInspect . Ports [ 0 ] . HostIP , containerInspect . Ports [ 0 ] . HostPort )
2020-07-06 03:29:48 +02:00
} )
2020-08-06 15:40:36 +02:00
t . Run ( "ps" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
l := out [ len ( out ) - 1 ]
2020-08-10 15:28:05 +02:00
assert . Assert ( t , strings . Contains ( l , container ) , "Looking for %q in line: %s" , container , l )
2020-08-06 15:40:36 +02:00
assert . Assert ( t , strings . Contains ( l , "nginx" ) )
assert . Assert ( t , strings . Contains ( l , "Running" ) )
assert . Assert ( t , strings . Contains ( l , hostIP + ":80->80/tcp" ) )
} )
2020-06-30 16:33:36 +02:00
2020-08-06 15:40:36 +02:00
t . Run ( "http get" , func ( t * testing . T ) {
2020-10-01 17:27:57 +02:00
output := HTTPGetWithRetry ( t , endpoint , http . StatusOK , 2 * time . Second , 20 * time . Second )
assert . Assert ( t , strings . Contains ( output , testFileContent ) , "Actual content: " + output )
2020-08-06 15:40:36 +02:00
} )
2020-07-10 13:00:41 +02:00
2020-08-06 15:40:36 +02:00
t . Run ( "logs" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "logs" , container )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : "GET" } )
} )
2020-06-30 16:33:36 +02:00
2020-08-06 15:40:36 +02:00
t . Run ( "exec" , func ( t * testing . T ) {
2020-10-14 14:35:00 +02:00
res := c . RunDockerOrExitError ( "exec" , container , "pwd" )
assert . Assert ( t , strings . Contains ( res . Stdout ( ) , "/" ) )
2020-06-30 16:33:36 +02:00
2020-08-24 10:23:14 +02:00
res = c . RunDockerOrExitError ( "exec" , container , "echo" , "fail_with_argument" )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected {
ExitCode : 1 ,
Err : "ACI exec command does not accept arguments to the command. Only the binary should be specified" ,
} )
2020-06-30 16:33:36 +02:00
} )
2020-08-06 15:40:36 +02:00
t . Run ( "logs follow" , func ( t * testing . T ) {
cmd := c . NewDockerCmd ( "logs" , "--follow" , container )
res := icmd . StartCmd ( cmd )
2020-07-02 15:43:16 +02:00
2020-08-06 15:40:36 +02:00
checkUp := func ( t poll . LogT ) poll . Result {
r , _ := http . Get ( endpoint + "/is_up" )
if r != nil && r . StatusCode == http . StatusNotFound {
return poll . Success ( )
2020-07-02 15:43:16 +02:00
}
2020-08-06 15:40:36 +02:00
return poll . Continue ( "waiting for container to serve request" )
}
poll . WaitOn ( t , checkUp , poll . WithDelay ( 1 * time . Second ) , poll . WithTimeout ( 60 * time . Second ) )
assert . Assert ( t , ! strings . Contains ( res . Stdout ( ) , "/test" ) )
checkLogs := func ( t poll . LogT ) poll . Result {
if strings . Contains ( res . Stdout ( ) , "/test" ) {
return poll . Success ( )
2020-07-02 15:43:16 +02:00
}
2020-08-06 15:40:36 +02:00
return poll . Continue ( "waiting for logs to contain /test" )
}
2020-07-02 15:43:16 +02:00
2020-08-06 15:40:36 +02:00
// Do request on /test
go func ( ) {
time . Sleep ( 3 * time . Second )
_ , _ = http . Get ( endpoint + "/test" )
} ( )
2020-07-02 15:43:16 +02:00
2020-08-06 15:40:36 +02:00
poll . WaitOn ( t , checkLogs , poll . WithDelay ( 3 * time . Second ) , poll . WithTimeout ( 20 * time . Second ) )
2020-07-02 15:43:16 +02:00
2020-08-06 15:40:36 +02:00
if runtime . GOOS == "windows" {
err := res . Cmd . Process . Kill ( )
assert . NilError ( t , err )
} else {
err := res . Cmd . Process . Signal ( syscall . SIGTERM )
assert . NilError ( t , err )
}
2020-07-02 15:43:16 +02:00
} )
2020-08-11 15:46:29 +02:00
t . Run ( "rm a running container" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerOrExitError ( "rm" , container )
2020-08-11 15:46:29 +02:00
res . Assert ( t , icmd . Expected {
2020-08-11 17:00:37 +02:00
Err : fmt . Sprintf ( "Error: you cannot remove a running container %s. Stop the container before attempting removal or force remove" , container ) ,
2020-08-11 15:46:29 +02:00
ExitCode : 1 ,
} )
} )
t . Run ( "force rm" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "rm" , "-f" , container )
2020-08-21 17:34:02 +02:00
res . Assert ( t , icmd . Expected { Out : container } )
2020-08-11 15:46:29 +02:00
2020-08-06 15:40:36 +02:00
checkStopped := func ( t poll . LogT ) poll . Result {
2020-08-24 10:23:14 +02:00
res := c . RunDockerOrExitError ( "inspect" , container )
2020-08-06 15:40:36 +02:00
if res . ExitCode == 1 {
return poll . Success ( )
}
return poll . Continue ( "waiting for container to stop" )
}
poll . WaitOn ( t , checkStopped , poll . WithDelay ( 5 * time . Second ) , poll . WithTimeout ( 60 * time . Second ) )
2020-05-13 09:00:48 +02:00
} )
2020-07-07 18:29:24 +02:00
}
2020-08-06 15:40:36 +02:00
func TestContainerRunAttached ( t * testing . T ) {
c := NewParallelE2eCLI ( t , binDir )
2020-10-02 18:16:13 +02:00
_ , groupID , location := setupTestResourceGroup ( t , c )
2020-08-06 15:40:36 +02:00
// Used in subtests
var (
2021-05-19 17:00:13 +02:00
container = "test-container"
2020-09-21 12:42:44 +02:00
endpoint string
followLogsProcess * icmd . Result
2020-08-06 15:40:36 +02:00
)
t . Run ( "run attached limits" , func ( t * testing . T ) {
2020-09-21 12:42:44 +02:00
dnsLabelName := "nginx-" + groupID
fqdn := dnsLabelName + "." + location + ".azurecontainer.io"
2020-08-06 15:40:36 +02:00
cmd := c . NewDockerCmd (
"run" ,
"--name" , container ,
"--restart" , "on-failure" ,
"--memory" , "0.1G" , "--cpus" , "0.1" ,
"-p" , "80:80" ,
2020-09-21 12:42:44 +02:00
"--domainname" ,
dnsLabelName ,
2020-10-01 12:56:47 +02:00
"nginx" ,
2020-08-06 15:40:36 +02:00
)
2020-09-04 15:09:57 +02:00
followLogsProcess = icmd . StartCmd ( cmd )
2020-08-06 15:40:36 +02:00
checkRunning := func ( t poll . LogT ) poll . Result {
2020-08-24 10:23:14 +02:00
res := c . RunDockerOrExitError ( "inspect" , container )
2020-10-09 12:28:48 +02:00
if res . ExitCode == 0 && strings . Contains ( res . Stdout ( ) , ` "Status": "Running" ` ) && ! strings . Contains ( res . Stdout ( ) , ` "HostIP": "" ` ) {
2020-08-06 15:40:36 +02:00
return poll . Success ( )
}
2020-09-21 12:42:44 +02:00
return poll . Continue ( "waiting for container to be running, current inspect result: \n%s" , res . Combined ( ) )
2020-08-06 15:40:36 +02:00
}
2021-01-28 10:25:22 +01:00
poll . WaitOn ( t , checkRunning , poll . WithDelay ( 5 * time . Second ) , poll . WithTimeout ( 180 * time . Second ) )
2020-08-06 15:40:36 +02:00
2020-08-24 10:23:14 +02:00
inspectRes := c . RunDockerCmd ( "inspect" , container )
2020-08-06 15:40:36 +02:00
2020-11-12 15:16:30 +01:00
containerInspect , err := parseContainerInspect ( inspectRes . Stdout ( ) )
2020-08-06 15:40:36 +02:00
assert . NilError ( t , err )
assert . Equal ( t , containerInspect . Platform , "Linux" )
2020-10-07 18:25:01 +02:00
assert . Equal ( t , containerInspect . HostConfig . CPULimit , 0.1 )
assert . Equal ( t , containerInspect . HostConfig . MemoryLimit , uint64 ( 107374182 ) )
2020-10-08 17:03:26 +02:00
assert . Equal ( t , containerInspect . HostConfig . CPUReservation , 0.1 )
assert . Equal ( t , containerInspect . HostConfig . MemoryReservation , uint64 ( 107374182 ) )
2020-10-07 18:25:01 +02:00
assert . Equal ( t , containerInspect . HostConfig . RestartPolicy , containers . RestartPolicyOnFailure )
2020-08-06 15:40:36 +02:00
assert . Assert ( t , is . Len ( containerInspect . Ports , 1 ) )
2020-08-10 15:28:05 +02:00
port := containerInspect . Ports [ 0 ]
2020-10-09 12:28:48 +02:00
assert . Assert ( t , port . HostIP != "" , "empty hostIP, inspect: \n" + inspectRes . Stdout ( ) )
2020-08-10 15:28:05 +02:00
assert . Equal ( t , port . ContainerPort , uint32 ( 80 ) )
assert . Equal ( t , port . HostPort , uint32 ( 80 ) )
2020-09-21 12:42:44 +02:00
assert . Equal ( t , containerInspect . Config . FQDN , fqdn )
endpoint = fmt . Sprintf ( "http://%s:%d" , fqdn , port . HostPort )
2020-08-06 15:40:36 +02:00
2020-09-04 15:09:57 +02:00
assert . Assert ( t , ! strings . Contains ( followLogsProcess . Stdout ( ) , "/test" ) )
2020-10-29 17:23:10 +01:00
HTTPGetWithRetry ( t , endpoint + "/test" , http . StatusNotFound , 2 * time . Second , 60 * time . Second )
2020-05-18 11:00:09 +02:00
2020-08-06 15:40:36 +02:00
checkLog := func ( t poll . LogT ) poll . Result {
2020-09-04 15:09:57 +02:00
if strings . Contains ( followLogsProcess . Stdout ( ) , "/test" ) {
2020-08-06 15:40:36 +02:00
return poll . Success ( )
}
return poll . Continue ( "waiting for logs to contain /test" )
}
poll . WaitOn ( t , checkLog , poll . WithDelay ( 1 * time . Second ) , poll . WithTimeout ( 20 * time . Second ) )
2020-05-06 15:28:03 +02:00
} )
2020-05-05 17:55:53 +02:00
2020-08-11 17:49:02 +02:00
t . Run ( "stop wrong container" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerOrExitError ( "stop" , "unknown-container" )
2020-08-11 17:49:02 +02:00
res . Assert ( t , icmd . Expected {
Err : "Error: container unknown-container not found" ,
ExitCode : 1 ,
} )
} )
t . Run ( "stop container" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "stop" , container )
2020-08-11 17:49:02 +02:00
res . Assert ( t , icmd . Expected { Out : container } )
2020-09-04 15:09:57 +02:00
waitForStatus ( t , c , container , "Terminated" , "Node Stopped" )
} )
t . Run ( "check we stoppped following logs" , func ( t * testing . T ) {
// nolint errcheck
followLogsStopped := waitWithTimeout ( func ( ) { followLogsProcess . Cmd . Process . Wait ( ) } , 10 * time . Second )
assert . NilError ( t , followLogsStopped , "Follow logs process did not stop after container is stopped" )
2020-08-11 17:49:02 +02:00
} )
2020-08-12 11:59:36 +02:00
t . Run ( "ps stopped container with --all" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "ps" , container )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-08-12 11:59:36 +02:00
assert . Assert ( t , is . Len ( out , 1 ) )
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "ps" , "--all" , container )
2020-11-27 16:15:13 +01:00
out = Lines ( res . Stdout ( ) )
2020-08-12 11:59:36 +02:00
assert . Assert ( t , is . Len ( out , 2 ) )
} )
2020-09-04 15:09:57 +02:00
t . Run ( "restart container" , func ( t * testing . T ) {
2021-01-11 11:15:17 +01:00
res := c . RunDockerOrExitError ( "start" , container )
2021-01-06 11:13:22 +01:00
//Flaky errors on restart : Code="ContainerGroupTransitioning" Message="The container group 'test-container' is still transitioning, please retry later."
if res . ExitCode != 0 && strings . Contains ( res . Stderr ( ) , ` Code="ContainerGroupTransitioning" ` ) {
2021-01-28 10:25:22 +01:00
res = c . RunDockerOrExitError ( "rm" , "-f" , container )
if strings . Contains ( res . Stderr ( ) , "unsupported protocol scheme" ) { // ...
time . Sleep ( 1 * time . Second )
c . RunDockerCmd ( "rm" , "-f" , container )
}
2021-02-25 17:37:09 +01:00
go func ( ) { // this specific call to run sometimes does not come back when in this weird state, but the container is actually running fine
c . RunDockerCmd ( "run" ,
"--name" , container , // don't reuse the container name, this container is in a weird state and blocks everything
"--memory" , "0.1G" , "--cpus" , "0.1" ,
"nginx" )
fmt . Printf ( " [%s] Finished docker run %s\n" , t . Name ( ) , container )
} ( )
waitForStatus ( t , c , container , convert . StatusRunning )
2021-01-28 10:25:22 +01:00
} else {
res . Assert ( t , icmd . Expected { Out : container } )
waitForStatus ( t , c , container , convert . StatusRunning )
2021-01-06 11:13:22 +01:00
}
2020-08-12 14:33:58 +02:00
} )
2020-10-14 16:06:45 +02:00
t . Run ( "prune dry run" , func ( t * testing . T ) {
res := c . RunDockerCmd ( "prune" , "--dry-run" )
2020-10-15 12:49:43 +02:00
assert . Equal ( t , "Resources that would be deleted:\nTotal CPUs reclaimed: 0.00, total memory reclaimed: 0.00 GB\n" , res . Stdout ( ) )
2020-12-09 16:15:43 +01:00
res = c . RunDockerOrExitError ( "prune" , "--dry-run" , "--force" )
if strings . Contains ( res . Stderr ( ) , "unsupported protocol scheme" ) { //Flaky strange error on azure SDK call happening only during prune --force
time . Sleep ( 1 * time . Second )
res = c . RunDockerCmd ( "prune" , "--dry-run" , "--force" )
}
2020-10-15 12:49:43 +02:00
assert . Equal ( t , "Resources that would be deleted:\n" + container + "\nTotal CPUs reclaimed: 0.10, total memory reclaimed: 0.10 GB\n" , res . Stdout ( ) )
2020-10-14 16:06:45 +02:00
} )
2020-08-12 14:33:58 +02:00
2020-10-14 16:06:45 +02:00
t . Run ( "prune" , func ( t * testing . T ) {
res := c . RunDockerCmd ( "prune" )
2020-10-15 12:49:43 +02:00
assert . Equal ( t , "Deleted resources:\nTotal CPUs reclaimed: 0.00, total memory reclaimed: 0.00 GB\n" , res . Stdout ( ) )
2020-10-14 16:06:45 +02:00
res = c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
l := Lines ( res . Stdout ( ) )
2020-10-14 16:06:45 +02:00
assert . Equal ( t , 2 , len ( l ) )
2020-12-15 15:44:53 +01:00
res = c . RunDockerOrExitError ( "prune" , "--force" )
if strings . Contains ( res . Stderr ( ) , "unsupported protocol scheme" ) { //Flaky strange error on azure SDK call happening only during prune --force
time . Sleep ( 1 * time . Second )
res = c . RunDockerCmd ( "prune" , "--force" )
2021-01-06 11:05:05 +01:00
// After the retry, it seems prune has sometimes actually been executed, and we get zero thigs to delete again...
assert . Assert ( t , res . Stdout ( ) == "Deleted resources:\n" + container + "\nTotal CPUs reclaimed: 0.10, total memory reclaimed: 0.10 GB\n" ||
res . Stdout ( ) == "Deleted resources:\nTotal CPUs reclaimed: 0.00, total memory reclaimed: 0.00 GB\n" , res . Stdout ( ) )
} else {
assert . Equal ( t , "Deleted resources:\n" + container + "\nTotal CPUs reclaimed: 0.10, total memory reclaimed: 0.10 GB\n" , res . Stdout ( ) )
2020-12-15 15:44:53 +01:00
}
2020-10-14 16:06:45 +02:00
res = c . RunDockerCmd ( "ps" , "--all" )
2020-11-27 16:15:13 +01:00
l = Lines ( res . Stdout ( ) )
2020-10-14 16:06:45 +02:00
assert . Equal ( t , 1 , len ( l ) )
2020-05-06 17:14:53 +02:00
} )
2020-08-06 15:40:36 +02:00
}
2020-05-06 15:28:03 +02:00
2020-10-01 16:03:22 +02:00
func overwriteFileStorageAccount ( t * testing . T , absComposefileName string , storageAccount string ) {
data , err := ioutil . ReadFile ( absComposefileName )
assert . NilError ( t , err )
2020-10-06 17:46:13 +02:00
override := strings . Replace ( string ( data ) , "dockertestvolumeaccount" , storageAccount , 1 )
err = ioutil . WriteFile ( absComposefileName , [ ] byte ( override ) , 0644 )
2020-10-01 16:03:22 +02:00
assert . NilError ( t , err )
}
2020-08-06 15:40:36 +02:00
2020-11-03 12:04:33 +01:00
func TestUpSecretsResources ( t * testing . T ) {
2020-10-08 17:03:26 +02:00
const (
2020-11-03 12:04:33 +01:00
composeProjectName = "aci_test"
2020-11-03 14:17:55 +01:00
web1 = composeProjectName + "_web1"
web2 = composeProjectName + "_web2"
2020-10-12 08:40:46 +02:00
secret1Name = "mytarget1"
secret1Value = "myPassword1\n"
secret2Name = "mysecret2"
secret2Value = "another_password\n"
)
2021-01-19 13:55:19 +01:00
composefilePath := filepath . Join ( "aci_secrets_resources" , "compose.yml" )
2020-10-12 08:40:46 +02:00
c := NewParallelE2eCLI ( t , binDir )
_ , _ , _ = setupTestResourceGroup ( t , c )
t . Run ( "compose up" , func ( t * testing . T ) {
2021-02-08 09:50:17 +01:00
c . RunDockerCmd ( "compose" , "-f" , composefilePath , "--project-name" , composeProjectName , "up" )
2020-10-12 08:40:46 +02:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-11-03 14:17:55 +01:00
// Check 2 containers running
2020-11-03 12:04:33 +01:00
assert . Assert ( t , is . Len ( out , 3 ) )
} )
2020-10-12 08:40:46 +02:00
2020-11-03 12:04:33 +01:00
t . Cleanup ( func ( ) {
2021-02-08 09:50:17 +01:00
c . RunDockerCmd ( "compose" , "--project-name" , composeProjectName , "down" )
2020-11-03 12:04:33 +01:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-11-03 12:04:33 +01:00
assert . Equal ( t , len ( out ) , 1 )
} )
2020-10-12 08:40:46 +02:00
2020-11-03 14:17:55 +01:00
res := c . RunDockerCmd ( "inspect" , web1 )
2020-11-12 15:16:30 +01:00
web1Inspect , err := parseContainerInspect ( res . Stdout ( ) )
2020-11-03 14:17:55 +01:00
assert . NilError ( t , err )
res = c . RunDockerCmd ( "inspect" , web2 )
2020-11-12 15:16:30 +01:00
web2Inspect , err := parseContainerInspect ( res . Stdout ( ) )
2020-11-03 12:04:33 +01:00
assert . NilError ( t , err )
2020-11-03 14:17:55 +01:00
t . Run ( "read secrets in service 1" , func ( t * testing . T ) {
assert . Assert ( t , is . Len ( web1Inspect . Ports , 1 ) )
endpoint := fmt . Sprintf ( "http://%s:%d" , web1Inspect . Ports [ 0 ] . HostIP , web1Inspect . Ports [ 0 ] . HostPort )
2020-10-12 08:40:46 +02:00
output := HTTPGetWithRetry ( t , endpoint + "/" + secret1Name , http . StatusOK , 2 * time . Second , 20 * time . Second )
2020-10-12 15:51:48 +02:00
// replace windows carriage return
output = strings . ReplaceAll ( output , "\r" , "" )
2020-10-12 08:40:46 +02:00
assert . Equal ( t , output , secret1Value )
output = HTTPGetWithRetry ( t , endpoint + "/" + secret2Name , http . StatusOK , 2 * time . Second , 20 * time . Second )
2020-10-12 15:51:48 +02:00
output = strings . ReplaceAll ( output , "\r" , "" )
2020-10-12 08:40:46 +02:00
assert . Equal ( t , output , secret2Value )
2020-11-03 12:04:33 +01:00
} )
2020-10-12 08:40:46 +02:00
2020-11-03 14:17:55 +01:00
t . Run ( "read secrets in service 2" , func ( t * testing . T ) {
assert . Assert ( t , is . Len ( web2Inspect . Ports , 1 ) )
endpoint := fmt . Sprintf ( "http://%s:%d" , web2Inspect . Ports [ 0 ] . HostIP , web2Inspect . Ports [ 0 ] . HostPort )
output := HTTPGetWithRetry ( t , endpoint + "/" + secret2Name , http . StatusOK , 2 * time . Second , 20 * time . Second )
output = strings . ReplaceAll ( output , "\r" , "" )
assert . Equal ( t , output , secret2Value )
HTTPGetWithRetry ( t , endpoint + "/" + secret1Name , http . StatusNotFound , 2 * time . Second , 20 * time . Second )
} )
2020-11-03 12:04:33 +01:00
t . Run ( "check resource limits" , func ( t * testing . T ) {
2020-11-03 14:17:55 +01:00
assert . Equal ( t , web1Inspect . HostConfig . CPULimit , 0.7 )
assert . Equal ( t , web1Inspect . HostConfig . MemoryLimit , uint64 ( 1073741824 ) )
assert . Equal ( t , web1Inspect . HostConfig . CPUReservation , 0.5 )
assert . Equal ( t , web1Inspect . HostConfig . MemoryReservation , uint64 ( 536870912 ) )
2020-11-03 12:04:33 +01:00
assert . Equal ( t , web2Inspect . HostConfig . CPULimit , 0.5 )
assert . Equal ( t , web2Inspect . HostConfig . MemoryLimit , uint64 ( 751619276 ) )
assert . Equal ( t , web2Inspect . HostConfig . CPUReservation , 0.5 )
assert . Equal ( t , web2Inspect . HostConfig . MemoryReservation , uint64 ( 751619276 ) )
2020-10-12 08:40:46 +02:00
} )
2020-11-10 12:20:21 +01:00
t . Run ( "check healthchecks inspect" , func ( t * testing . T ) {
2020-11-12 15:16:30 +01:00
assert . Assert ( t , web1Inspect . Healthcheck != nil )
assert . Equal ( t , time . Duration ( * web1Inspect . Healthcheck . Interval ) , 5 * time . Second )
2020-11-10 12:20:21 +01:00
assert . DeepEqual ( t , web1Inspect . Healthcheck . Test , [ ] string { "curl" , "-f" , "http://localhost:80/healthz" } )
2020-11-12 15:16:30 +01:00
assert . Assert ( t , web2Inspect . Healthcheck == nil )
2020-11-10 12:20:21 +01:00
} )
t . Run ( "healthcheck restart failed app" , func ( t * testing . T ) {
endpoint := fmt . Sprintf ( "http://%s:%d" , web1Inspect . Ports [ 0 ] . HostIP , web1Inspect . Ports [ 0 ] . HostPort )
HTTPGetWithRetry ( t , endpoint + "/failtestserver" , http . StatusOK , 3 * time . Second , 3 * time . Second )
logs := c . RunDockerCmd ( "logs" , web1 ) . Combined ( )
assert . Assert ( t , strings . Contains ( logs , "GET /healthz" ) )
assert . Assert ( t , strings . Contains ( logs , "GET /failtestserver" ) )
assert . Assert ( t , strings . Contains ( logs , "Server failing" ) )
checkLogsReset := func ( logt poll . LogT ) poll . Result {
res := c . RunDockerOrExitError ( "logs" , web1 )
if res . ExitCode == 0 &&
! strings . Contains ( res . Combined ( ) , "GET /failtestserver" ) &&
strings . Contains ( res . Combined ( ) , "Listening on port 80" ) &&
strings . Contains ( res . Combined ( ) , "GET /healthz" ) {
return poll . Success ( )
}
return poll . Continue ( "Logs not reset by healcheck restart\n" + res . Combined ( ) )
}
poll . WaitOn ( t , checkLogsReset , poll . WithDelay ( 5 * time . Second ) , poll . WithTimeout ( 90 * time . Second ) )
res := c . RunDockerCmd ( "inspect" , web1 )
2020-11-12 15:16:30 +01:00
web1Inspect , err = parseContainerInspect ( res . Stdout ( ) )
2020-11-10 12:20:21 +01:00
assert . Equal ( t , web1Inspect . Status , "Running" )
} )
2020-10-12 08:40:46 +02:00
}
2020-10-01 16:03:22 +02:00
func TestUpUpdate ( t * testing . T ) {
2020-08-06 15:40:36 +02:00
const (
2020-10-01 16:03:22 +02:00
composeProjectName = "acidemo"
serverContainer = composeProjectName + "_web"
wordsContainer = composeProjectName + "_words"
dbContainer = composeProjectName + "_db"
2020-08-06 15:40:36 +02:00
)
2020-10-01 16:03:22 +02:00
var (
singlePortVolumesComposefile = "aci_demo_port_volumes.yaml"
2020-10-29 12:42:59 +01:00
multiPortComposefile = "demo_multi_port.yaml"
2020-10-01 16:03:22 +02:00
)
c := NewParallelE2eCLI ( t , binDir )
sID , groupID , location := setupTestResourceGroup ( t , c )
2020-11-04 01:47:07 +01:00
composeAccountName := strings . ToLower ( strings . ReplaceAll ( groupID , "-" , "" ) + "sa" )
2020-10-01 16:03:22 +02:00
dstDir := filepath . Join ( os . TempDir ( ) , "e2e-aci-volume-" + composeAccountName )
2021-01-19 13:55:19 +01:00
err := fileutil . CopyDirs ( "aci-demo" , dstDir )
2020-10-01 16:03:22 +02:00
assert . NilError ( t , err )
t . Cleanup ( func ( ) {
assert . NilError ( t , os . RemoveAll ( dstDir ) )
} )
2021-01-19 13:55:19 +01:00
_ , err = fileutils . CopyFile ( filepath . Join ( filepath . Join ( "aci-demo" ) , multiPortComposefile ) , filepath . Join ( dstDir , multiPortComposefile ) )
2020-10-29 12:42:59 +01:00
assert . NilError ( t , err )
2020-10-01 16:03:22 +02:00
singlePortVolumesComposefile = filepath . Join ( dstDir , singlePortVolumesComposefile )
overwriteFileStorageAccount ( t , singlePortVolumesComposefile , composeAccountName )
2020-10-06 17:46:13 +02:00
multiPortComposefile = filepath . Join ( dstDir , multiPortComposefile )
2020-08-06 15:40:36 +02:00
2020-10-08 10:47:27 +02:00
volumeID := composeAccountName + "/" + fileshareName
2020-11-04 01:47:07 +01:00
const (
testFileName = "msg.txt"
testFileContent = "VOLUME_OK"
projectName = "acidemo"
)
var (
dnsLabelName = "nginx-" + groupID
fqdn = dnsLabelName + "." + location + ".azurecontainer.io"
)
2020-10-01 16:03:22 +02:00
2020-11-04 01:47:07 +01:00
t . Run ( "compose up" , func ( t * testing . T ) {
2020-10-01 16:03:22 +02:00
aciContext := store . AciContext {
SubscriptionID : sID ,
Location : location ,
ResourceGroup : groupID ,
}
2021-02-08 09:50:17 +01:00
c . RunDockerCmd ( "compose" , "-f" , singlePortVolumesComposefile , "--project-name" , projectName , "up" , "--domainname" , dnsLabelName )
2020-11-04 01:47:07 +01:00
// Volume should be autocreated by the "compose up"
2020-10-01 16:03:22 +02:00
uploadTestFile ( t , aciContext , composeAccountName , fileshareName , testFileName , testFileContent )
2020-11-04 01:47:07 +01:00
} )
2020-10-01 16:03:22 +02:00
2020-11-04 01:47:07 +01:00
t . Cleanup ( func ( ) {
c . RunDockerCmd ( "volume" , "rm" , volumeID )
} )
2020-09-21 17:05:49 +02:00
2020-11-04 01:47:07 +01:00
t . Run ( "check deployed compose app" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
// Check three containers are running
assert . Assert ( t , is . Len ( out , 4 ) )
webRunning := false
for _ , l := range out {
if strings . Contains ( l , serverContainer ) {
webRunning = true
2020-12-07 14:46:36 +01:00
assert . Check ( t , strings . Contains ( l , ":80->80/tcp" ) )
2020-06-24 18:20:27 +02:00
}
2020-06-18 10:03:28 +02:00
}
2020-10-06 17:46:13 +02:00
assert . Assert ( t , webRunning , "web container not running ; ps:\n" + res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "inspect" , serverContainer )
2020-08-06 15:40:36 +02:00
2020-11-12 15:16:30 +01:00
containerInspect , err := parseContainerInspect ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
assert . NilError ( t , err )
assert . Assert ( t , is . Len ( containerInspect . Ports , 1 ) )
endpoint := fmt . Sprintf ( "http://%s:%d" , containerInspect . Ports [ 0 ] . HostIP , containerInspect . Ports [ 0 ] . HostPort )
2020-10-01 17:27:57 +02:00
output := HTTPGetWithRetry ( t , endpoint + "/words/noun" , http . StatusOK , 2 * time . Second , 20 * time . Second )
assert . Assert ( t , strings . Contains ( output , ` "word": ` ) )
2020-09-21 17:05:49 +02:00
endpoint = fmt . Sprintf ( "http://%s:%d" , fqdn , containerInspect . Ports [ 0 ] . HostPort )
2020-10-01 17:27:57 +02:00
HTTPGetWithRetry ( t , endpoint + "/words/noun" , http . StatusOK , 2 * time . Second , 20 * time . Second )
2020-10-01 16:03:22 +02:00
body := HTTPGetWithRetry ( t , endpoint + "/volume_test/" + testFileName , http . StatusOK , 2 * time . Second , 20 * time . Second )
assert . Assert ( t , strings . Contains ( body , testFileContent ) )
2020-10-08 10:47:27 +02:00
// Try to remove the volume while it's still in use
res = c . RunDockerOrExitError ( "volume" , "rm" , volumeID )
res . Assert ( t , icmd . Expected {
ExitCode : 1 ,
Err : fmt . Sprintf ( ` Error: volume "%s/%s" is used in container group %q ` ,
composeAccountName , fileshareName , projectName ) ,
} )
} )
2020-06-18 10:03:28 +02:00
2020-08-28 14:38:51 +02:00
t . Run ( "compose ps" , func ( t * testing . T ) {
2021-02-08 09:50:17 +01:00
res := c . RunDockerCmd ( "compose" , "--project-name" , composeProjectName , "ps" , "--quiet" )
2020-11-27 16:15:13 +01:00
l := Lines ( res . Stdout ( ) )
2020-10-12 11:03:43 +02:00
assert . Assert ( t , is . Len ( l , 3 ) )
2021-02-08 09:50:17 +01:00
res = c . RunDockerCmd ( "compose" , "--project-name" , composeProjectName , "ps" )
2020-11-27 16:15:13 +01:00
l = Lines ( res . Stdout ( ) )
2020-10-12 11:03:43 +02:00
assert . Assert ( t , is . Len ( l , 4 ) )
2020-08-28 14:38:51 +02:00
var wordsDisplayed , webDisplayed , dbDisplayed bool
2020-10-12 11:03:43 +02:00
for _ , line := range l {
2020-08-28 14:38:51 +02:00
fields := strings . Fields ( line )
2020-12-08 14:38:21 +01:00
name := fields [ 0 ]
switch name {
2020-08-28 14:38:51 +02:00
case wordsContainer :
wordsDisplayed = true
2020-12-08 14:38:21 +01:00
assert . Equal ( t , fields [ 2 ] , "Running" )
2020-08-28 14:38:51 +02:00
case dbContainer :
dbDisplayed = true
2020-12-08 14:38:21 +01:00
assert . Equal ( t , fields [ 2 ] , "Running" )
2020-08-28 14:38:51 +02:00
case serverContainer :
webDisplayed = true
2020-12-08 14:38:21 +01:00
assert . Equal ( t , fields [ 2 ] , "Running" )
2020-08-28 14:38:51 +02:00
assert . Check ( t , strings . Contains ( fields [ 3 ] , ":80->80/tcp" ) )
}
}
2020-12-08 14:38:21 +01:00
assert . Check ( t , webDisplayed , "webDisplayed" + res . Stdout ( ) )
assert . Check ( t , wordsDisplayed , "wordsDisplayed" + res . Stdout ( ) )
assert . Check ( t , dbDisplayed , "dbDisplayed" + res . Stdout ( ) )
2020-08-28 14:38:51 +02:00
assert . Check ( t , webDisplayed && wordsDisplayed && dbDisplayed , "\n%s\n" , res . Stdout ( ) )
} )
2020-09-07 16:24:21 +02:00
t . Run ( "compose ls" , func ( t * testing . T ) {
2020-10-12 11:03:43 +02:00
res := c . RunDockerCmd ( "compose" , "ls" , "--quiet" )
2020-11-27 16:15:13 +01:00
l := Lines ( res . Stdout ( ) )
2020-10-12 11:03:43 +02:00
assert . Assert ( t , is . Len ( l , 1 ) )
res = c . RunDockerCmd ( "compose" , "ls" )
2020-11-27 16:15:13 +01:00
l = Lines ( res . Stdout ( ) )
2020-10-12 11:03:43 +02:00
assert . Equal ( t , 2 , len ( l ) )
fields := strings . Fields ( l [ 1 ] )
2020-09-07 16:24:21 +02:00
assert . Equal ( t , 2 , len ( fields ) )
assert . Equal ( t , fields [ 0 ] , composeProjectName )
assert . Equal ( t , "Running" , fields [ 1 ] )
} )
2020-08-06 15:40:36 +02:00
t . Run ( "logs web" , func ( t * testing . T ) {
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "logs" , serverContainer )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : "Listening on port 80" } )
2020-05-06 15:28:03 +02:00
} )
2020-07-07 18:29:24 +02:00
2020-08-06 15:40:36 +02:00
t . Run ( "update" , func ( t * testing . T ) {
2021-02-08 09:50:17 +01:00
c . RunDockerCmd ( "compose" , "-f" , multiPortComposefile , "--project-name" , composeProjectName , "up" )
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
// Check three containers are running
assert . Assert ( t , is . Len ( out , 4 ) )
for _ , cName := range [ ] string { serverContainer , wordsContainer } {
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "inspect" , cName )
2020-08-06 15:40:36 +02:00
2020-11-12 15:16:30 +01:00
containerInspect , err := parseContainerInspect ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
assert . NilError ( t , err )
assert . Assert ( t , is . Len ( containerInspect . Ports , 1 ) )
endpoint := fmt . Sprintf ( "http://%s:%d" , containerInspect . Ports [ 0 ] . HostIP , containerInspect . Ports [ 0 ] . HostPort )
var route string
switch cName {
case serverContainer :
route = "/words/noun"
assert . Equal ( t , containerInspect . Ports [ 0 ] . HostPort , uint32 ( 80 ) )
assert . Equal ( t , containerInspect . Ports [ 0 ] . ContainerPort , uint32 ( 80 ) )
case wordsContainer :
route = "/noun"
assert . Equal ( t , containerInspect . Ports [ 0 ] . HostPort , uint32 ( 8080 ) )
assert . Equal ( t , containerInspect . Ports [ 0 ] . ContainerPort , uint32 ( 8080 ) )
}
2020-10-01 17:27:57 +02:00
HTTPGetWithRetry ( t , endpoint + route , http . StatusOK , 1 * time . Second , 60 * time . Second )
2020-07-07 12:17:46 +02:00
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "ps" )
2020-08-06 15:40:36 +02:00
p := containerInspect . Ports [ 0 ]
res . Assert ( t , icmd . Expected {
Out : fmt . Sprintf ( "%s:%d->%d/tcp" , p . HostIP , p . HostPort , p . ContainerPort ) ,
} )
}
2020-07-07 12:17:46 +02:00
} )
2020-08-06 15:40:36 +02:00
t . Run ( "down" , func ( t * testing . T ) {
2021-02-08 09:50:17 +01:00
c . RunDockerCmd ( "compose" , "--project-name" , composeProjectName , "down" )
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
assert . Equal ( t , len ( out ) , 1 )
2020-05-05 17:55:53 +02:00
} )
2020-08-06 15:40:36 +02:00
}
2020-05-05 17:55:53 +02:00
2020-10-13 12:28:22 +02:00
/ *
2020-08-06 15:40:36 +02:00
func TestRunEnvVars ( t * testing . T ) {
c := NewParallelE2eCLI ( t , binDir )
2020-10-02 18:16:13 +02:00
_ , _ , _ = setupTestResourceGroup ( t , c )
2020-08-06 15:40:36 +02:00
t . Run ( "run" , func ( t * testing . T ) {
cmd := c . NewDockerCmd (
"run" , "-d" ,
"-e" , "MYSQL_ROOT_PASSWORD=rootpwd" ,
"-e" , "MYSQL_DATABASE=mytestdb" ,
"-e" , "MYSQL_USER" ,
"-e" , "MYSQL_PASSWORD=userpwd" ,
2020-09-10 11:59:49 +02:00
"-e" , "DATASOURCE_URL=jdbc:mysql://mydb.mysql.database.azure.com/db1?useSSL=true&requireSSL=false&serverTimezone=America/Recife" ,
2020-08-06 15:40:36 +02:00
"mysql:5.7" ,
)
cmd . Env = append ( cmd . Env , "MYSQL_USER=user1" )
res := icmd . RunCmd ( cmd )
res . Assert ( t , icmd . Success )
2020-11-27 16:15:13 +01:00
out := Lines ( res . Stdout ( ) )
2020-08-06 15:40:36 +02:00
container := strings . TrimSpace ( out [ len ( out ) - 1 ] )
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "inspect" , container )
2020-08-06 15:40:36 +02:00
containerInspect , err := ParseContainerInspect ( res . Stdout ( ) )
assert . NilError ( t , err )
2020-09-10 11:59:49 +02:00
assert . Assert ( t , containerInspect . Config != nil , "nil container config" )
assert . Assert ( t , containerInspect . Config . Env != nil , "nil container env variables" )
2020-08-06 15:40:36 +02:00
assert . Equal ( t , containerInspect . Image , "mysql:5.7" )
2020-09-10 11:59:49 +02:00
envVars := containerInspect . Config . Env
assert . Equal ( t , len ( envVars ) , 5 )
assert . Equal ( t , envVars [ "MYSQL_ROOT_PASSWORD" ] , "rootpwd" )
assert . Equal ( t , envVars [ "MYSQL_DATABASE" ] , "mytestdb" )
assert . Equal ( t , envVars [ "MYSQL_USER" ] , "user1" )
assert . Equal ( t , envVars [ "MYSQL_PASSWORD" ] , "userpwd" )
assert . Equal ( t , envVars [ "DATASOURCE_URL" ] , "jdbc:mysql://mydb.mysql.database.azure.com/db1?useSSL=true&requireSSL=false&serverTimezone=America/Recife" )
2020-08-06 15:40:36 +02:00
check := func ( t poll . LogT ) poll . Result {
2020-08-24 10:23:14 +02:00
res := c . RunDockerOrExitError ( "logs" , container )
2020-08-06 15:40:36 +02:00
if strings . Contains ( res . Stdout ( ) , "Giving user user1 access to schema mytestdb" ) {
return poll . Success ( )
}
2020-10-13 09:24:45 +02:00
return poll . Continue ( "waiting for DB container to be up\n%s" , res . Combined ( ) )
2020-08-06 15:40:36 +02:00
}
2020-10-12 16:25:49 +02:00
poll . WaitOn ( t , check , poll . WithDelay ( 5 * time . Second ) , poll . WithTimeout ( 90 * time . Second ) )
2020-05-05 17:55:53 +02:00
} )
}
2020-10-13 12:28:22 +02:00
* /
2020-05-05 17:55:53 +02:00
2020-10-02 18:16:13 +02:00
func setupTestResourceGroup ( t * testing . T , c * E2eCLI ) ( string , string , string ) {
2020-08-10 15:28:05 +02:00
startTime := strconv . Itoa ( int ( time . Now ( ) . Unix ( ) ) )
2020-10-01 16:03:22 +02:00
rg := "E2E-" + t . Name ( ) + "-" + startTime [ 5 : ]
2020-08-10 16:38:59 +02:00
azureLogin ( t , c )
2020-08-06 15:40:36 +02:00
sID := getSubscriptionID ( t )
2020-10-02 18:16:13 +02:00
location := getTestLocation ( )
err := createResourceGroup ( t , sID , rg , location )
2020-08-06 15:40:36 +02:00
assert . Check ( t , is . Nil ( err ) )
t . Cleanup ( func ( ) {
2020-08-28 14:38:51 +02:00
if err := deleteResourceGroup ( t , rg ) ; err != nil {
2020-08-06 15:40:36 +02:00
t . Error ( err )
}
} )
2020-10-02 18:16:13 +02:00
createAciContextAndUseIt ( t , c , sID , rg , location )
2020-08-06 15:40:36 +02:00
// Check nothing is running
2020-08-24 10:23:14 +02:00
res := c . RunDockerCmd ( "ps" )
2020-11-27 16:15:13 +01:00
assert . Assert ( t , is . Len ( Lines ( res . Stdout ( ) ) , 1 ) )
2020-10-02 18:16:13 +02:00
return sID , rg , location
2020-07-07 18:29:24 +02:00
}
2020-08-28 14:38:51 +02:00
func deleteResourceGroup ( t * testing . T , rgName string ) error {
fmt . Printf ( " [%s] deleting resource group %s\n" , t . Name ( ) , rgName )
2020-08-06 15:40:36 +02:00
ctx := context . TODO ( )
helper := aci . NewACIResourceGroupHelper ( )
models , err := helper . GetSubscriptionIDs ( ctx )
if err != nil {
return err
}
if len ( models ) == 0 {
return errors . New ( "unable to delete resource group: no models" )
}
return helper . DeleteAsync ( ctx , * models [ 0 ] . SubscriptionID , rgName )
2020-07-07 18:29:24 +02:00
}
2020-08-10 16:38:59 +02:00
func azureLogin ( t * testing . T , c * E2eCLI ) {
2020-07-07 18:29:24 +02:00
// in order to create new service principal and get these 3 values : `az ad sp create-for-rbac --name 'TestServicePrincipal' --sdk-auth`
clientID := os . Getenv ( "AZURE_CLIENT_ID" )
clientSecret := os . Getenv ( "AZURE_CLIENT_SECRET" )
tenantID := os . Getenv ( "AZURE_TENANT_ID" )
2020-08-19 09:53:25 +02:00
assert . Check ( t , clientID != "" , "AZURE_CLIENT_ID must not be empty" )
assert . Check ( t , clientSecret != "" , "AZURE_CLIENT_SECRET must not be empty" )
assert . Check ( t , tenantID != "" , "AZURE_TENANT_ID must not be empty" )
2020-08-24 10:23:14 +02:00
c . RunDockerCmd ( "login" , "azure" , "--client-id" , clientID , "--client-secret" , clientSecret , "--tenant-id" , tenantID )
2020-07-07 18:29:24 +02:00
}
2020-08-06 15:40:36 +02:00
func getSubscriptionID ( t * testing . T ) string {
ctx := context . TODO ( )
helper := aci . NewACIResourceGroupHelper ( )
models , err := helper . GetSubscriptionIDs ( ctx )
assert . Check ( t , is . Nil ( err ) )
assert . Check ( t , len ( models ) == 1 )
return * models [ 0 ] . SubscriptionID
2020-07-07 18:29:24 +02:00
}
2020-10-02 18:16:13 +02:00
func createResourceGroup ( t * testing . T , sID , rgName string , location string ) error {
2020-08-28 14:38:51 +02:00
fmt . Printf ( " [%s] creating resource group %s\n" , t . Name ( ) , rgName )
2020-08-06 15:40:36 +02:00
helper := aci . NewACIResourceGroupHelper ( )
_ , err := helper . CreateOrUpdate ( context . TODO ( ) , sID , rgName , resources . Group { Location : to . StringPtr ( location ) } )
return err
2020-07-07 18:29:24 +02:00
}
2020-10-02 18:16:13 +02:00
func createAciContextAndUseIt ( t * testing . T , c * E2eCLI , sID , rgName string , location string ) {
2020-08-26 18:03:57 +02:00
res := c . RunDockerCmd ( "context" , "create" , "aci" , contextName , "--subscription-id" , sID , "--resource-group" , rgName , "--location" , location )
res . Assert ( t , icmd . Expected { Out : "Successfully created aci context \"" + contextName + "\"" } )
res = c . RunDockerCmd ( "context" , "use" , contextName )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : contextName } )
2020-08-24 10:23:14 +02:00
res = c . RunDockerCmd ( "context" , "ls" )
2020-08-06 15:40:36 +02:00
res . Assert ( t , icmd . Expected { Out : contextName + " *" } )
2020-07-07 18:29:24 +02:00
}
2020-08-06 15:40:36 +02:00
func uploadFile ( t * testing . T , cred azfile . SharedKeyCredential , baseURL , fileName , content string ) {
2020-05-19 11:29:48 +02:00
fURL , err := url . Parse ( baseURL + "/" + fileName )
2020-08-06 15:40:36 +02:00
assert . NilError ( t , err )
fileURL := azfile . NewFileURL ( * fURL , azfile . NewPipeline ( & cred , azfile . PipelineOptions { } ) )
err = azfile . UploadBufferToAzureFile ( context . TODO ( ) , [ ] byte ( content ) , fileURL , azfile . UploadToAzureFileOptions { } )
assert . NilError ( t , err )
2020-05-20 15:57:10 +02:00
}
2020-08-06 15:40:36 +02:00
func getContainerName ( stdout string ) string {
2020-11-27 16:15:13 +01:00
out := Lines ( stdout )
2020-08-06 15:40:36 +02:00
return strings . TrimSpace ( out [ len ( out ) - 1 ] )
2020-06-30 17:29:47 +02:00
}
2020-08-12 14:33:58 +02:00
2020-08-18 10:57:50 +02:00
func waitForStatus ( t * testing . T , c * E2eCLI , containerID string , statuses ... string ) {
2020-08-14 09:39:10 +02:00
checkStopped := func ( logt poll . LogT ) poll . Result {
2021-02-26 09:51:10 +01:00
res := c . RunDockerOrExitError ( "inspect" , containerID )
if res . Error != nil {
return poll . Continue ( "Error while inspecting container %s: %s" , containerID , res . Combined ( ) )
}
2020-11-12 15:16:30 +01:00
containerInspect , err := parseContainerInspect ( res . Stdout ( ) )
2020-08-14 09:39:10 +02:00
assert . NilError ( t , err )
2020-08-18 10:57:50 +02:00
for _ , status := range statuses {
if containerInspect . Status == status {
return poll . Success ( )
}
2020-08-12 14:33:58 +02:00
}
2020-08-18 10:57:50 +02:00
return poll . Continue ( "Status %s != %s (expected) for container %s" , containerInspect . Status , statuses , containerID )
2020-08-12 14:33:58 +02:00
}
2020-08-14 12:42:19 +02:00
poll . WaitOn ( t , checkStopped , poll . WithDelay ( 5 * time . Second ) , poll . WithTimeout ( 90 * time . Second ) )
2020-08-12 14:33:58 +02:00
}
2020-09-04 15:09:57 +02:00
2020-11-12 15:16:30 +01:00
func parseContainerInspect ( stdout string ) ( * cmd . ContainerInspectView , error ) {
var res cmd . ContainerInspectView
rdr := bytes . NewReader ( [ ] byte ( stdout ) )
if err := json . NewDecoder ( rdr ) . Decode ( & res ) ; err != nil {
return nil , err
}
return & res , nil
}
2020-09-04 15:09:57 +02:00
func waitWithTimeout ( blockingCall func ( ) , timeout time . Duration ) error {
c := make ( chan struct { } )
go func ( ) {
defer close ( c )
blockingCall ( )
} ( )
select {
case <- c :
return nil
case <- time . After ( timeout ) :
return fmt . Errorf ( "Timed out after %s" , timeout )
}
}