From 9bc5a5ff0b4bbbf8ef2fab821bc974909d276982 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 11 Feb 2016 16:55:33 +0100 Subject: First bunch of tests --- domain_config.go | 16 +++++- domain_config_test.go | 64 ++++++++++++++++++++++ domains.go | 49 +++++++++-------- domains_test.go | 35 ++++++++++++ main.go | 19 +++++-- shared/pages/.hidden.group/project/.gitkeep | 0 shared/pages/group.deleted/is_file.txt | 0 .../group.deleted/project.deleted/public/.gitkeep | 0 .../group.hidden/.hidden.project/public/.gitkeep | 0 .../project.internal/public/.gitkeep | 0 shared/pages/group.no.projects/.gitkeep | 0 shared/pages/group.no.public/project/.gitkeep | 0 shared/pages/group/group.test.io/.gitkeep | 0 shared/pages/group/group.test.io/config.json | 15 +++++ shared/pages/group/group.test.io/public/.gitkeep | 0 shared/pages/is_file | 0 16 files changed, 168 insertions(+), 30 deletions(-) create mode 100644 domain_config_test.go create mode 100644 domains_test.go create mode 100644 shared/pages/.hidden.group/project/.gitkeep create mode 100644 shared/pages/group.deleted/is_file.txt create mode 100644 shared/pages/group.deleted/project.deleted/public/.gitkeep create mode 100644 shared/pages/group.hidden/.hidden.project/public/.gitkeep create mode 100644 shared/pages/group.internal/project.internal/public/.gitkeep create mode 100644 shared/pages/group.no.projects/.gitkeep create mode 100644 shared/pages/group.no.public/project/.gitkeep create mode 100644 shared/pages/group/group.test.io/.gitkeep create mode 100644 shared/pages/group/group.test.io/config.json create mode 100644 shared/pages/group/group.test.io/public/.gitkeep create mode 100644 shared/pages/is_file diff --git a/domain_config.go b/domain_config.go index d16ca9ad..feaceab5 100644 --- a/domain_config.go +++ b/domain_config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "os" "path/filepath" + "strings" ) type domainConfig struct { @@ -16,10 +17,21 @@ type domainsConfig struct { Domains []domainConfig } +func (c *domainConfig) Valid() bool { + if c.Domain == "" { + return false + } + + // TODO: better sanitize domain + domain := strings.ToLower(c.Domain) + rootDomain := "." + strings.ToLower(*pagesDomain) + return !strings.HasSuffix(domain, rootDomain) +} + func (c *domainsConfig) Read(group, project string) (err error) { - configFile, err := os.Open(filepath.Join(*pagesRoot, project, group, "config.json")) + configFile, err := os.Open(filepath.Join(*pagesRoot, group, project, "config.json")) if err != nil { - return nil + return err } defer configFile.Close() diff --git a/domain_config_test.go b/domain_config_test.go new file mode 100644 index 00000000..5417fa86 --- /dev/null +++ b/domain_config_test.go @@ -0,0 +1,64 @@ +package main + +import ( + "testing" + "os" + "io/ioutil" + + "github.com/stretchr/testify/assert" + "path/filepath" +) + +const configFile = "shared/pages/test-group/test-project/config.json" +const invalidConfig = `{"Domains":{}}` +const validConfig = `{"Domains":[{"Domain":"test"}]}` + +func TestDomainConfigValidness(t *testing.T) { + d := domainConfig{} + assert.False(t, d.Valid()) + + d = domainConfig{Domain: "test"} + assert.True(t, d.Valid()) + + *pagesDomain = "gitlab.io" + + d = domainConfig{Domain: "test"} + assert.True(t, d.Valid()) + + d = domainConfig{Domain: "test.gitlab.io"} + assert.False(t, d.Valid()) + + d = domainConfig{Domain: "test.test.gitlab.io"} + assert.False(t, d.Valid()) + + d = domainConfig{Domain: "test.testgitlab.io"} + assert.True(t, d.Valid()) + + d = domainConfig{Domain: "test.GitLab.Io"} + assert.False(t, d.Valid()) +} + +func TestDomainConfigRead(t *testing.T) { + d := domainsConfig{} + err := d.Read("test-group", "test-project") + assert.Error(t, err) + + os.MkdirAll(filepath.Dir(configFile), 0700) + defer os.RemoveAll("shared/pages/test-group") + + d = domainsConfig{} + err = d.Read("test-group", "test-project") + assert.Error(t, err) + + err = ioutil.WriteFile(configFile, []byte(invalidConfig), 0600) + assert.NoError(t, err) + d = domainsConfig{} + err = d.Read("test-group", "test-project") + assert.Error(t, err) + + err = ioutil.WriteFile(configFile, []byte(validConfig), 0600) + assert.NoError(t, err) + d = domainsConfig{} + err = d.Read("test-group", "test-project") + assert.NoError(t, err) +} diff --git a/domains.go b/domains.go index 49058fb0..e36cc3d9 100644 --- a/domains.go +++ b/domains.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "errors" "io/ioutil" "log" "os" @@ -11,32 +10,18 @@ import ( "time" ) -type domains map[string]domain +type domains map[string]*domain type domainsUpdater func(domains domains) -func isDomainAllowed(domain string) bool { - if domain == "" { - return false - } - // TODO: better sanitize domain - domain = strings.ToLower(domain) - rootDomain := "." + strings.ToLower(*pagesDomain) - return !strings.HasPrefix(domain, rootDomain) -} - func (d domains) addDomain(group, project string, config *domainConfig) error { - newDomain := domain{ + newDomain := &domain{ Group: group, Project: project, Config: config, } if config != nil { - if !isDomainAllowed(config.Domain) { - return errors.New("domain name is not allowed") - } - d[config.Domain] = newDomain } else { domainName := group + "." + *pagesDomain @@ -58,23 +43,38 @@ func (d domains) readProjects(group string) (count int) { } for _, project := range fis { + // Ignore non directories if !project.IsDir() { continue } + + // Ignore hidden projects if strings.HasPrefix(project.Name(), ".") { continue } - count++ + // Ignore projects that have .deleted in name + if strings.HasSuffix(project.Name(), ".deleted") { + continue + } - var config domainsConfig - err := config.Read(group, project.Name()) + // Ignore projects without public + _, err := os.Lstat(filepath.Join(*pagesRoot, group, project.Name(), "public")) if err != nil { continue } - for _, domainConfig := range config.Domains { - d.addDomain(group, project.Name(), &domainConfig) + count++ + + var config domainsConfig + err = config.Read(group, project.Name()) + log.Println(err) + if err == nil { + for _, domainConfig := range config.Domains { + if domainConfig.Valid() { + d.addDomain(group, project.Name(), &domainConfig) + } + } } } return @@ -108,7 +108,7 @@ func (d domains) ReadGroups() error { return nil } -func watchDomains(updater domainsUpdater) { +func watchDomains(updater domainsUpdater, interval time.Duration) { lastUpdate := []byte("no-update") for { @@ -116,7 +116,7 @@ func watchDomains(updater domainsUpdater) { if bytes.Equal(lastUpdate, update) { if err != nil { log.Println("Failed to read update timestamp:", err) - time.Sleep(time.Second) + time.Sleep(interval) } continue } @@ -131,5 +131,6 @@ func watchDomains(updater domainsUpdater) { if updater != nil { updater(domains) } + time.Sleep(interval) } } diff --git a/domains_test.go b/domains_test.go new file mode 100644 index 00000000..5c16aa14 --- /dev/null +++ b/domains_test.go @@ -0,0 +1,35 @@ +package main + +import ( + "testing" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" +) + +func TestReadProjects(t *testing.T) { + *pagesDomain = "test.io" + + d := make(domains) + err := d.ReadGroups() + require.NoError(t, err) + + var domains []string + for domain, _ := range d { + domains = append(domains, domain) + } + + expectedDomains := []string{ + "group.test.io", + "group.internal.test.io", + "test.domain.com", // from config.json + "other.domain.com", + } + + for _, expected := range domains { + assert.Contains(t, domains, expected) + } + + for _, actual := range domains { + assert.Contains(t, expectedDomains, actual) + } +} diff --git a/main.go b/main.go index b89cec0c..a1e41ad4 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" "sync" + "time" ) // VERSION stores the information about the semantic version of application @@ -31,6 +32,14 @@ const xForwardedProtoHTTPS = "https" type theApp struct { domains domains + lock sync.RWMutex +} + +func (a *theApp) domain(host string) *domain { + a.lock.RLock() + defer a.lock.RUnlock() + domain, _ := a.domains[host] + return domain } func (a *theApp) ServeTLS(ch *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -39,7 +48,7 @@ func (a *theApp) ServeTLS(ch *tls.ClientHelloInfo) (*tls.Certificate, error) { } host := strings.ToLower(ch.ServerName) - if domain, ok := a.domains[host]; ok { + if domain := a.domain(host); domain != nil { tls, _ := domain.ensureCertificate() return tls, nil } @@ -63,9 +72,9 @@ func (a *theApp) serveContent(ww http.ResponseWriter, r *http.Request, https boo } host := strings.ToLower(r.Host) - domain, ok := a.domains[host] + domain := a.domain(host) - if !ok { + if domain == nil { http.NotFound(&w, r) return } @@ -87,7 +96,9 @@ func (a *theApp) ServeProxy(ww http.ResponseWriter, r *http.Request) { func (a *theApp) UpdateDomains(domains domains) { fmt.Printf("Domains: %v", domains) + a.lock.Lock() a.domains = domains + a.lock.Unlock() } func main() { @@ -134,7 +145,7 @@ func main() { }() } - go watchDomains(app.UpdateDomains) + go watchDomains(app.UpdateDomains, time.Second) wg.Wait() } diff --git a/shared/pages/.hidden.group/project/.gitkeep b/shared/pages/.hidden.group/project/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group.deleted/is_file.txt b/shared/pages/group.deleted/is_file.txt new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group.deleted/project.deleted/public/.gitkeep b/shared/pages/group.deleted/project.deleted/public/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group.hidden/.hidden.project/public/.gitkeep b/shared/pages/group.hidden/.hidden.project/public/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group.internal/project.internal/public/.gitkeep b/shared/pages/group.internal/project.internal/public/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group.no.projects/.gitkeep b/shared/pages/group.no.projects/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group.no.public/project/.gitkeep b/shared/pages/group.no.public/project/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group/group.test.io/.gitkeep b/shared/pages/group/group.test.io/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/group/group.test.io/config.json b/shared/pages/group/group.test.io/config.json new file mode 100644 index 00000000..1643525d --- /dev/null +++ b/shared/pages/group/group.test.io/config.json @@ -0,0 +1,15 @@ +{ + "Domains": [ + { + "Domain": "test.domain.com" + }, + { + "Domain": "my.test.io" + }, + { + "Domain": "other.domain.com", + "Certificate": "test", + "Certificate": "key" + } + ] +} diff --git a/shared/pages/group/group.test.io/public/.gitkeep b/shared/pages/group/group.test.io/public/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/pages/is_file b/shared/pages/is_file new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3