Skip to content

Commit

Permalink
validate sitenames, validate config during init, fixes ddev#86 (ddev#339
Browse files Browse the repository at this point in the history
)

* normalize sitenames on config write and read

* improve comment on regexp

* switch to a validation approach vs replacing invalid values, add config validation to Init
  • Loading branch information
tannerjfco authored and rfay committed Jun 27, 2017
1 parent 01a8068 commit 07ffa4f
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 14 deletions.
75 changes: 61 additions & 14 deletions pkg/ddevapp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"path/filepath"
"strings"

"regexp"

log "github.com/Sirupsen/logrus"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/drud/ddev/pkg/appports"
Expand All @@ -31,6 +33,9 @@ const DDevTLD = "ddev.local"
// AllowedAppTypes lists the types of site/app that can be used.
var AllowedAppTypes = []string{"drupal7", "drupal8", "wordpress"}

// Regexp pattern to determine if a hostname is valid per RFC 1123.
var hostRegex = regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)

// Config defines the yaml config file format for ddev applications
type Config struct {
APIVersion string `yaml:"APIVersion"`
Expand Down Expand Up @@ -149,21 +154,15 @@ func (c *Config) Config() error {
"Existing config": awsutil.Prettify(c),
}).Debug("Configuring application")

namePrompt := "Project name"
if c.Name == "" {
dir, err := os.Getwd()
if err == nil {
c.Name = filepath.Base(dir)
}
err := c.namePrompt()
if err != nil {
return err
}

namePrompt = fmt.Sprintf("%s (%s)", namePrompt, c.Name)
// Define an application name.
fmt.Print(namePrompt + ": ")
c.Name = util.GetInput(c.Name)

err := c.docrootPrompt()
util.CheckErr(err)
err = c.docrootPrompt()
if err != nil {
return err
}

err = c.appTypePrompt()
if err != nil {
Expand All @@ -178,6 +177,29 @@ func (c *Config) Config() error {
return nil
}

// Validate ensures the configuraton meets ddev's requirements.
func (c *Config) Validate() error {
// validate docroot
fullPath := filepath.Join(c.AppRoot, c.Docroot)
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
return fmt.Errorf("no directory could be found at %s. Please enter a valid docroot in your configuration", fullPath)
}

// validate hostname
match := hostRegex.MatchString(c.Hostname())
if !match {
return fmt.Errorf("%s is not a valid hostname. Please enter a site name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", c.Hostname())
}

// validate apptype
match = IsAllowedAppType(c.AppType)
if !match {
return fmt.Errorf("%s is not a valid apptype", c.AppType)
}

return nil
}

// DockerComposeYAMLPath returns the absolute path to where the docker-compose.yaml should exist for this app configuration.
func (c *Config) DockerComposeYAMLPath() string {
return filepath.Join(c.AppRoot, ".ddev", "docker-compose.yaml")
Expand Down Expand Up @@ -233,8 +255,33 @@ func (c *Config) RenderComposeYAML() (string, error) {
return doc.String(), err
}

// Define an application name.
func (c *Config) namePrompt() error {
namePrompt := "Project name"
if c.Name == "" {
dir, err := os.Getwd()
// if working directory name is invalid for hostnames, we shouldn't suggest it
if err == nil && hostRegex.MatchString(filepath.Base(dir)) {
c.Name = filepath.Base(dir)
}
}

namePrompt = fmt.Sprintf("%s (%s)", namePrompt, c.Name)
fmt.Print(namePrompt + ": ")
c.Name = util.GetInput(c.Name)

match := hostRegex.MatchString(c.Hostname())
if !match {
fmt.Printf("%s is not a valid hostname. Please enter a site name that will allow for a valid hostname.\n See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements \n", c.Hostname())
c.Name = ""
return c.namePrompt()
}

return nil
}

// Determine the document root.
func (c *Config) docrootPrompt() error {
// Determine the document root.
fmt.Printf("\nThe docroot is the directory from which your site is served. This is a relative path from your application root (%s)\n", c.AppRoot)
fmt.Println("You may leave this value blank if your site files are in the application root")
var docrootPrompt = "Docroot Location"
Expand Down
32 changes: 32 additions & 0 deletions pkg/ddevapp/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,35 @@ func TestRead(t *testing.T) {
assert.Equal(c.AppType, "drupal8")
assert.Equal(c.WebImage, "test/testimage:latest")
}

// TestValidate tests validation of configuration values.
func TestValidate(t *testing.T) {
assert := assert.New(t)

cwd, err := os.Getwd()
assert.NoError(err)

c := &Config{
Name: "TestValidate",
AppRoot: cwd,
Docroot: "testing",
AppType: "wordpress",
}

err = c.Validate()
assert.NoError(err)

c.Name = "Invalid!"
err = c.Validate()
assert.EqualError(err, fmt.Sprintf("%s is not a valid hostname. Please enter a site name in your configuration that will allow for a valid hostname. See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames for valid hostname requirements", c.Hostname()))

c.Name = "valid"
c.Docroot = "invalid"
err = c.Validate()
assert.EqualError(err, fmt.Sprintf("no directory could be found at %s. Please enter a valid docroot in your configuration", filepath.Join(cwd, c.Docroot)))

c.Docroot = "testing"
c.AppType = "potato"
err = c.Validate()
assert.EqualError(err, fmt.Sprintf("%s is not a valid apptype", c.AppType))
}
5 changes: 5 additions & 0 deletions pkg/plugins/platform/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ func (l *LocalApp) Init(basePath string) error {
return fmt.Errorf("could not find an active ddev configuration, have you run 'ddev config'?: %v", err)
}

err = config.Validate()
if err != nil {
return err
}

l.AppConfig = config

web, err := l.FindContainerByType("web")
Expand Down

0 comments on commit 07ffa4f

Please sign in to comment.