Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VM creation to use parent domain for subdomains #522

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions trellis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"

"github.com/roots/trellis-cli/dns"
"github.com/weppos/publicsuffix-go/publicsuffix"
Expand Down Expand Up @@ -162,3 +163,44 @@ func (c *Config) AllHostsByDomain() map[string][]dns.Host {

return hostsByDomain
}

// GetParentDomain returns the parent domain of a given site name
func GetParentDomain(siteName string) string {
parsedDomain, err := publicsuffix.Parse(siteName)
if err != nil {
return siteName
}

// If it's already a top-level domain (no subdomain)
if parsedDomain.TRD == "" {
return siteName
}

// Return the domain without the subdomain part
return fmt.Sprintf("%s.%s", parsedDomain.SLD, parsedDomain.TLD)
}

// MainSiteName returns the name of the main site that should be used for VM naming
// It returns the first site that matches the parent domain, or the first site if no match
func (c *Config) MainSiteName() string {
if len(c.WordPressSites) == 0 {
return DefaultSiteName
}

// Get all site names and sort them for consistent results
siteNames := []string{}
for name := range c.WordPressSites {
siteNames = append(siteNames, name)
}
sort.Strings(siteNames)

// First look for the first non-subdomain site
for _, name := range siteNames {
if !strings.Contains(name, ".") || GetParentDomain(name) == name {
return name
}
}

// If all sites are subdomains, use the first site
return siteNames[0]
}
109 changes: 109 additions & 0 deletions trellis/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,112 @@ wordpress_sites:
t.Errorf("expected %v, got %v", expectedHosts, allHosts)
}
}

func TestGetParentDomain(t *testing.T) {
cases := []struct {
siteName string
parentDomain string
}{
{
"example.com",
"example.com",
},
{
"www.example.com",
"example.com",
},
{
"sub.example.com",
"example.com",
},
{
"deep.sub.example.com",
"example.com",
},
{
"example.co.uk",
"example.co.uk",
},
{
"sub.example.co.uk",
"example.co.uk",
},
}

for _, tc := range cases {
result := GetParentDomain(tc.siteName)
if result != tc.parentDomain {
t.Errorf("GetParentDomain(%s) => expected %s, got %s", tc.siteName, tc.parentDomain, result)
}
}
}

func TestMainSiteName(t *testing.T) {
cases := []struct {
name string
configYaml string
expectedResult string
}{
{
"single site",
`
wordpress_sites:
example.com:
site_hosts:
- canonical: example.com
`,
"example.com",
},
{
"main domain and subdomain",
`
wordpress_sites:
example.com:
site_hosts:
- canonical: example.com
sub.example.com:
site_hosts:
- canonical: sub.example.com
`,
"example.com",
},
{
"multiple subdomains only",
`
wordpress_sites:
sub.example.com:
site_hosts:
- canonical: sub.example.com
blog.example.com:
site_hosts:
- canonical: blog.example.com
`,
"blog.example.com", // should return the first one alphabetically
},
{
"multiple domains",
`
wordpress_sites:
example.com:
site_hosts:
- canonical: example.com
another-example.com:
site_hosts:
- canonical: another-example.com
`,
"another-example.com", // should return the first one alphabetically
},
}

for _, tc := range cases {
config := &Config{}
if err := yaml.Unmarshal([]byte(tc.configYaml), &config); err != nil {
t.Fatal(err)
}

result := config.MainSiteName()
if result != tc.expectedResult {
t.Errorf("%s => expected %s, got %s", tc.name, tc.expectedResult, result)
}
}
}
22 changes: 14 additions & 8 deletions trellis/trellis.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,22 @@ func (t *Trellis) FindSiteNameFromEnvironment(environment string, siteNameArg st
return "", fmt.Errorf("Error: %s is not a valid site. Valid options are %s", siteNameArg, siteNames)
}

func (t *Trellis) MainSiteFromEnvironment(environment string) (string, *Site, error) {
sites := t.SiteNamesFromEnvironment(environment)

if len(sites) == 0 {
return "", nil, fmt.Errorf("Error: No sites found in %s environment", environment)
func (t *Trellis) MainSiteFromEnvironment(env string) (string, *Site, error) {
if _, ok := t.Environments[env]; !ok {
return "", nil, fmt.Errorf("environment %s not found", env)
}

name := sites[0]

return name, t.Environments[environment].WordPressSites[name], nil
config := t.Environments[env]

// Use the MainSiteName method to get the proper site name that should be used for VM naming
siteName := config.MainSiteName()
site, ok := config.WordPressSites[siteName]

if !ok {
return "", nil, fmt.Errorf("site %s not found in environment %s", siteName, env)
}

return siteName, site, nil
}

func (t *Trellis) getDefaultSiteNameFromEnvironment(environment string) (siteName string, err error) {
Expand Down
100 changes: 77 additions & 23 deletions trellis/trellis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,29 +240,83 @@ func TestSiteFromEnvironmentAndName(t *testing.T) {
}

func TestMainSiteFromEnvironment(t *testing.T) {
expected := &Site{}

environments := make(map[string]*Config)
environments["a"] = &Config{
WordPressSites: make(map[string]*Site),
}

environments["a"].WordPressSites["a1"] = expected
environments["a"].WordPressSites["a2"] = &Site{}
environments["a"].WordPressSites["a3"] = &Site{}

trellis := Trellis{
Environments: environments,
}

name, actual, _ := trellis.MainSiteFromEnvironment("a")

if name != "a1" {
t.Errorf("expected a1 got %s", name)
}

if actual != expected {
t.Error("expected site not returned")
cases := []struct {
name string
sites map[string]*Site
expectedSite string
shouldHaveError bool
}{
{
"single site",
map[string]*Site{
"example.com": {},
},
"example.com",
false,
},
{
"parent domain and subdomain",
map[string]*Site{
"example.com": {},
"sub.example.com": {},
"other.example.com": {},
},
"example.com",
false,
},
{
"only subdomains",
map[string]*Site{
"sub.example.com": {},
"blog.example.com": {},
},
"blog.example.com", // alphabetically first
false,
},
{
"multiple parent domains",
map[string]*Site{
"example.com": {},
"another-example.com": {},
},
"another-example.com", // alphabetically first
false,
},
{
"no sites",
map[string]*Site{},
"",
true,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
environments := make(map[string]*Config)
environments["development"] = &Config{
WordPressSites: tc.sites,
}

trellis := Trellis{
Environments: environments,
}

name, _, err := trellis.MainSiteFromEnvironment("development")

if tc.shouldHaveError {
if err == nil {
t.Error("expected error but got nil")
}
} else {
if err != nil {
t.Errorf("unexpected error: %s", err)
}

if name != tc.expectedSite {
t.Errorf("expected site name '%s', got '%s'", tc.expectedSite, name)
}
}
})
}
}

Expand Down