Skip to content

Commit

Permalink
Improve router-compose and ssh-auth-compose handling, fixes #1779, fixes
Browse files Browse the repository at this point in the history
 #2476 (#2505)

* Refactor router.go so that a ddev stop before start won't break things,
fixes #1779
* the letsencrypt volume should not be created unless letsencrypt is being used
* Allow overriding router-compose, fixes #2476
* Allow overriding ssh-auth-compose, fixes #2476
* Add tests for overriding router-compose and ssh-auth-compose
  • Loading branch information
rfay committed Sep 17, 2020
1 parent 45ce7d4 commit 5845120
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 30 deletions.
4 changes: 4 additions & 0 deletions cmd/ddev/cmd/global_dotddev_assets/.gitignore
@@ -1,4 +1,8 @@
#ddev-generated
/.router-compose.yaml
/.router-composer-full.yaml
/.ssh-auth-compose.yaml
/.ssh-auth-compose-full.yaml
/.gitignore
/**/*.example
/**/README.*
Expand Down
2 changes: 1 addition & 1 deletion cmd/ddev/cmd/packrd/packed-packr.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 66 additions & 23 deletions pkg/ddevapp/router.go
Expand Up @@ -9,6 +9,7 @@ import (
"html/template"
"os"
"path"
"path/filepath"
"sort"
"strings"

Expand All @@ -25,20 +26,29 @@ const RouterProjectName = "ddev-router"
// RouterComposeYAMLPath returns the full filepath to the routers docker-compose yaml file.
func RouterComposeYAMLPath() string {
globalDir := globalconfig.GetGlobalDdevDir()
dest := path.Join(globalDir, "router-compose.yaml")
dest := path.Join(globalDir, ".router-compose.yaml")
return dest
}

// FullRenderedRouterComposeYAMLPath returns the path of the full rendered .router-compose-full.yaml
func FullRenderedRouterComposeYAMLPath() string {
globalDir := globalconfig.GetGlobalDdevDir()
dest := path.Join(globalDir, ".router-compose-full.yaml")
return dest
}

// StopRouterIfNoContainers stops the router if there are no ddev containers running.
func StopRouterIfNoContainers() error {

dest, err := generateRouterCompose()
if err != nil {
return err
}
containersRunning, err := ddevContainersRunning()
if err != nil {
return err
}

if !containersRunning {
dest := RouterComposeYAMLPath()
_, _, err = dockerutil.ComposeCmd([]string{dest}, "-p", RouterProjectName, "down")
return err
}
Expand All @@ -47,29 +57,57 @@ func StopRouterIfNoContainers() error {

// StartDdevRouter ensures the router is running.
func StartDdevRouter() error {
newExposedPorts := determineRouterPorts()
routerComposeFullPath, err := generateRouterCompose()
if err != nil {
return err
}
err = CheckRouterPorts()
if err != nil {
return fmt.Errorf("Unable to listen on required ports, %v,\nTroubleshooting suggestions at https://ddev.readthedocs.io/en/stable/users/troubleshooting/#unable-listen", err)
}

// run docker-compose up -d against the ddev-router full compose file
_, _, err = dockerutil.ComposeCmd([]string{routerComposeFullPath}, "-p", RouterProjectName, "up", "-d")
if err != nil {
return fmt.Errorf("failed to start ddev-router: %v", err)
}

// ensure we have a happy router
label := map[string]string{"com.docker.compose.service": "ddev-router"}
logOutput, err := dockerutil.ContainerWait(containerWaitTimeout, label)
if err != nil {
return fmt.Errorf("ddev-router failed to become ready; debug with 'docker logs ddev-router'; logOutput=%s, err=%v", logOutput, err)
}

return nil
}

routerComposePath := RouterComposeYAMLPath()
// generateRouterCompose() generates the ~/.ddev/.router-compose.yaml and ~/.ddev/.router-compose-full.yaml
func generateRouterCompose() (string, error) {
exposedPorts := determineRouterPorts()

routerComposeBasePath := RouterComposeYAMLPath()
routerComposeFullPath := FullRenderedRouterComposeYAMLPath()

var doc bytes.Buffer
f, ferr := os.Create(routerComposePath)
f, ferr := os.Create(routerComposeBasePath)
if ferr != nil {
return ferr
return "", ferr
}
defer util.CheckClose(f)

templ := template.New("routerTemplate")
templ, err := templ.Parse(DdevRouterTemplate)
if err != nil {
return err
return "", err
}

dockerIP, _ := dockerutil.GetDockerIP()

templateVars := map[string]interface{}{
"router_image": version.RouterImage,
"router_tag": version.RouterTag,
"ports": newExposedPorts,
"ports": exposedPorts,
"router_bind_all_interfaces": globalconfig.DdevGlobalConfig.RouterBindAllInterfaces,
"compose_version": version.DockerComposeFileFormatVersion,
"dockerIP": dockerIP,
Expand All @@ -79,29 +117,34 @@ func StartDdevRouter() error {
}

err = templ.Execute(&doc, templateVars)
util.CheckErr(err)
if err != nil {
return "", err
}
_, err = f.WriteString(doc.String())
util.CheckErr(err)

err = CheckRouterPorts()
if err != nil {
return fmt.Errorf("Unable to listen on required ports, %v,\nTroubleshooting suggestions at https://ddev.readthedocs.io/en/stable/users/troubleshooting/#unable-listen", err)
return "", err
}

// run docker-compose up -d against the ddev-router compose file
_, _, err = dockerutil.ComposeCmd([]string{routerComposePath}, "-p", RouterProjectName, "up", "-d")
fullHandle, err := os.Create(routerComposeFullPath)
if err != nil {
return fmt.Errorf("failed to start ddev-router: %v", err)
return "", err
}

// ensure we have a happy router
label := map[string]string{"com.docker.compose.service": "ddev-router"}
logOutput, err := dockerutil.ContainerWait(containerWaitTimeout, label)
userFiles, err := filepath.Glob(filepath.Join(globalconfig.GetGlobalDdevDir(), "router-compose.*.yaml"))
if err != nil {
return fmt.Errorf("ddev-router failed to become ready; debug with 'docker logs ddev-router'; logOutput=%s, err=%v", logOutput, err)
return "", err
}
files := append([]string{RouterComposeYAMLPath()}, userFiles...)
fullContents, _, err := dockerutil.ComposeCmd(files, "config")
if err != nil {
return "", err
}
_, err = fullHandle.WriteString(fullContents)
if err != nil {
return "", err
}

return nil
return routerComposeFullPath, nil
}

// FindDdevRouter usees FindContainerByLabels to get our router container and
Expand Down Expand Up @@ -158,7 +201,7 @@ func GetRouterStatus() (string, string) {
// determineRouterPorts returns a list of port mappings retrieved from running site
// containers defining VIRTUAL_PORT env var
func determineRouterPorts() []string {
var routerPorts []string
routerPorts := []string{"80"}
labels := map[string]string{
"com.ddev.platform": "ddev",
}
Expand Down
40 changes: 40 additions & 0 deletions pkg/ddevapp/router_test.go
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/drud/ddev/pkg/netutil"
"github.com/drud/ddev/pkg/nodeps"
"github.com/stretchr/testify/require"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -147,3 +149,41 @@ func TestLetsEncrypt(t *testing.T) {
_, _, err = dockerutil.Exec(container.ID, "test -f /etc/letsencrypt/options-ssl-nginx.conf")
assert.NoError(err)
}

// TestRouterConfigOverride tests that the ~/.ddev/.router-compose.yaml can be overridden
// with ~/.ddev/router-compose.*.yaml
func TestRouterConfigOverride(t *testing.T) {
assert := asrt.New(t)
pwd, _ := os.Getwd()
testDir := testcommon.CreateTmpDir(t.Name())
_ = os.Chdir(testDir)
overrideYaml := filepath.Join(globalconfig.GetGlobalDdevDir(), "router-compose.override.yaml")

testcommon.ClearDockerEnv()

app, err := ddevapp.NewApp(testDir, true, nodeps.ProviderDefault)
assert.NoError(err)
err = app.WriteConfig()
assert.NoError(err)
err = fileutil.CopyFile(filepath.Join(pwd, "testdata", t.Name(), "router-compose.override.yaml"), overrideYaml)
assert.NoError(err)

answer := fileutil.RandomFilenameBase()
os.Setenv("ANSWER", answer)
assert.NoError(err)
t.Cleanup(func() {
err = app.Stop(true, false)
assert.NoError(err)
err = os.Chdir(pwd)
assert.NoError(err)
_ = os.RemoveAll(testDir)
err = os.Remove(overrideYaml)
assert.NoError(err)
})

err = app.Start()
assert.NoError(err)

stdout, _, err := dockerutil.Exec("ddev-router", "bash -c 'echo $ANSWER'")
assert.Equal(answer+"\n", stdout)
}
37 changes: 31 additions & 6 deletions pkg/ddevapp/ssh_auth.go
Expand Up @@ -18,10 +18,18 @@ import (
// SSHAuthName is the "machine name" of the ddev-ssh-agent docker-compose service
const SSHAuthName = "ddev-ssh-agent"

// SSHAuthComposeYAMLPath returns the full filepath to the ssh-auth docker-compose yaml file.
// SSHAuthComposeYAMLPath returns the filepath to the base .ssh-auth-compose yaml file.
func SSHAuthComposeYAMLPath() string {
globalDir := globalconfig.GetGlobalDdevDir()
dest := path.Join(globalDir, "ssh-auth-compose.yaml")
dest := path.Join(globalDir, ".ssh-auth-compose.yaml")
return dest
}

// FullRenderedSSHAuthComposeYAMLPath returns the filepath to the rendered
//.ssh-auth-compose-full.yaml file.
func FullRenderedSSHAuthComposeYAMLPath() string {
globalDir := globalconfig.GetGlobalDdevDir()
dest := path.Join(globalDir, ".ssh-auth-compose-full.yaml")
return dest
}

Expand Down Expand Up @@ -92,10 +100,8 @@ func RemoveSSHAgentContainer() error {
// CreateSSHAuthComposeFile creates the docker-compose file for the ddev-ssh-agent
func CreateSSHAuthComposeFile() (string, error) {

sshAuthComposePath := SSHAuthComposeYAMLPath()

var doc bytes.Buffer
f, ferr := os.Create(sshAuthComposePath)
f, ferr := os.Create(SSHAuthComposeYAMLPath())
if ferr != nil {
return "", ferr
}
Expand Down Expand Up @@ -129,7 +135,26 @@ func CreateSSHAuthComposeFile() (string, error) {
util.CheckErr(err)
_, err = f.WriteString(doc.String())
util.CheckErr(err)
return sshAuthComposePath, nil

fullHandle, err := os.Create(FullRenderedSSHAuthComposeYAMLPath())
if err != nil {
return "", err
}

userFiles, err := filepath.Glob(filepath.Join(globalconfig.GetGlobalDdevDir(), "ssh-auth-compose.*.yaml"))
if err != nil {
return "", err
}
files := append([]string{SSHAuthComposeYAMLPath()}, userFiles...)
fullContents, _, err := dockerutil.ComposeCmd(files, "config")
if err != nil {
return "", err
}
_, err = fullHandle.WriteString(fullContents)
if err != nil {
return "", err
}
return FullRenderedSSHAuthComposeYAMLPath(), nil
}

// findDdevSSHAuth usees FindContainerByLabels to get our sshAuth container and
Expand Down
45 changes: 45 additions & 0 deletions pkg/ddevapp/ssh_auth_test.go
Expand Up @@ -5,6 +5,8 @@ import (
"github.com/drud/ddev/pkg/dockerutil"
"github.com/drud/ddev/pkg/exec"
"github.com/drud/ddev/pkg/fileutil"
"github.com/drud/ddev/pkg/globalconfig"
"github.com/drud/ddev/pkg/nodeps"
"github.com/drud/ddev/pkg/testcommon"
"github.com/drud/ddev/pkg/util"
"github.com/drud/ddev/pkg/version"
Expand Down Expand Up @@ -125,3 +127,46 @@ func TestSSHAuth(t *testing.T) {
runTime()
switchDir()
}

// TestSshAuthConfigOverride tests that the ~/.ddev/.ssh-auth-compose-compose.yaml can be overridden
// with ~/.ddev/ssh-auth-compose.*.yaml
func TestSshAuthConfigOverride(t *testing.T) {
assert := asrt.New(t)
pwd, _ := os.Getwd()
testDir := testcommon.CreateTmpDir(t.Name())
_ = os.Chdir(testDir)
overrideYaml := filepath.Join(globalconfig.GetGlobalDdevDir(), "ssh-auth-compose.override.yaml")

// Remove the ddev-ssh-agent, since the start code simply checks to see if it's
// running and doesn't restart it if it's running
_ = dockerutil.RemoveContainer("ddev-ssh-agent", 0)

testcommon.ClearDockerEnv()

app, err := ddevapp.NewApp(testDir, true, nodeps.ProviderDefault)
assert.NoError(err)
err = app.WriteConfig()
assert.NoError(err)
err = fileutil.CopyFile(filepath.Join(pwd, "testdata", t.Name(), "ssh-auth-compose.override.yaml"), overrideYaml)
assert.NoError(err)

answer := fileutil.RandomFilenameBase()
os.Setenv("ANSWER", answer)
assert.NoError(err)
assert.NoError(err)
t.Cleanup(func() {
err = app.Stop(true, false)
assert.NoError(err)
err = os.Chdir(pwd)
assert.NoError(err)
_ = os.RemoveAll(testDir)
err = os.Remove(overrideYaml)
assert.NoError(err)
})

err = app.Start()
assert.NoError(err)

stdout, _, err := dockerutil.Exec("ddev-ssh-agent", "bash -c 'echo $ANSWER'")
assert.Equal(answer+"\n", stdout)
}
2 changes: 2 additions & 0 deletions pkg/ddevapp/templates.go
Expand Up @@ -449,8 +449,10 @@ networks:
volumes:
ddev-global-cache:
name: ddev-global-cache
{{ if .letsencrypt }}
ddev-router-letsencrypt:
name: ddev-router-letsencrypt
{{ end }}
`

const DdevSSHAuthTemplate = `version: '{{ .compose_version }}'
Expand Down
@@ -0,0 +1,5 @@
version: '3.6'
services:
ddev-router:
environment:
- ANSWER
@@ -0,0 +1,5 @@
version: '3.6'
services:
ddev-ssh-agent:
environment:
- ANSWER

0 comments on commit 5845120

Please sign in to comment.