Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--internal/acme/acme.go4
-rw-r--r--internal/acme/acme_test.go2
-rw-r--r--internal/domain/config.go11
-rw-r--r--internal/domain/domain.go138
-rw-r--r--internal/domain/domain_test.go148
-rw-r--r--internal/domain/handler.go42
-rw-r--r--internal/domain/project.go10
-rw-r--r--internal/domain/resolver.go10
-rw-r--r--internal/serving/disk.go19
-rw-r--r--internal/serving/disk/group.go64
-rw-r--r--internal/serving/disk/handler.go11
-rw-r--r--internal/serving/disk/project.go58
-rw-r--r--internal/serving/disk/reader.go44
-rw-r--r--internal/serving/disk/serving.go47
-rw-r--r--internal/serving/handler.go16
-rw-r--r--internal/serving/serving.go29
-rw-r--r--internal/source/disk/custom.go24
-rw-r--r--internal/source/disk/domain_test.go (renamed from internal/source/disk/group_domain_test.go)86
-rw-r--r--internal/source/disk/group.go78
-rw-r--r--internal/source/disk/map.go38
-rw-r--r--internal/source/disk/map_test.go12
22 files changed, 390 insertions, 503 deletions
diff --git a/go.mod b/go.mod
index 2c4252a4..046398e2 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,8 @@ require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/karrick/godirwalk v1.10.12
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/namsral/flag v1.7.4-pre
github.com/prometheus/client_golang v1.1.0
github.com/rs/cors v1.7.0
diff --git a/internal/acme/acme.go b/internal/acme/acme.go
index 89881f34..a6be01de 100644
--- a/internal/acme/acme.go
+++ b/internal/acme/acme.go
@@ -18,7 +18,7 @@ type Middleware struct {
// Domain interface represent D from domain package
type Domain interface {
- HasAcmeChallenge(string) bool
+ HasAcmeChallenge(*http.Request, string) bool
}
// ServeAcmeChallenges identifies if request is acme-challenge and redirects to GitLab in that case
@@ -31,7 +31,7 @@ func (m *Middleware) ServeAcmeChallenges(w http.ResponseWriter, r *http.Request,
return false
}
- if domain.HasAcmeChallenge(filepath.Base(r.URL.Path)) {
+ if domain.HasAcmeChallenge(r, filepath.Base(r.URL.Path)) {
return false
}
diff --git a/internal/acme/acme_test.go b/internal/acme/acme_test.go
index c0daefeb..00932d3e 100644
--- a/internal/acme/acme_test.go
+++ b/internal/acme/acme_test.go
@@ -11,7 +11,7 @@ type domainStub struct {
hasAcmeChallenge bool
}
-func (d *domainStub) HasAcmeChallenge(_ string) bool {
+func (d *domainStub) HasAcmeChallenge(_ *http.Request, _ string) bool {
return d.hasAcmeChallenge
}
diff --git a/internal/domain/config.go b/internal/domain/config.go
deleted file mode 100644
index 040b2279..00000000
--- a/internal/domain/config.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package domain
-
-// ProjectConfig holds a custom project domain configuration
-type ProjectConfig struct {
- DomainName string
- Certificate string
- Key string
- HTTPSOnly bool
- ProjectID uint64
- AccessControl bool
-}
diff --git a/internal/domain/domain.go b/internal/domain/domain.go
index 361c3b87..1dd5be51 100644
--- a/internal/domain/domain.go
+++ b/internal/domain/domain.go
@@ -5,31 +5,23 @@ import (
"errors"
"net/http"
"sync"
- "time"
"gitlab.com/gitlab-org/gitlab-pages/internal/httperrors"
"gitlab.com/gitlab-org/gitlab-pages/internal/serving"
)
-// GroupConfig represents a per-request config for a group domain
-type GroupConfig interface {
- IsHTTPSOnly(*http.Request) bool
- HasAccessControl(*http.Request) bool
- IsNamespaceProject(*http.Request) bool
- ProjectID(*http.Request) uint64
- ProjectExists(*http.Request) bool
- ProjectWithSubpath(*http.Request) (string, string, error)
-}
-
// Domain is a domain that gitlab-pages can serve.
type Domain struct {
- Group string
- Project string
+ Name string
+ Location string
+ CertificateCert string
+ CertificateKey string
+ Customized bool // TODO we should get rid of this
- GroupConfig GroupConfig // handles group domain config
- ProjectConfig *ProjectConfig
+ Resolver Resolver
- serving serving.Serving
+ lookupPaths map[string]*Project
+ serving serving.Serving
certificate *tls.Certificate
certificateError error
@@ -38,15 +30,7 @@ type Domain struct {
// String implements Stringer.
func (d *Domain) String() string {
- if d.Group != "" && d.Project != "" {
- return d.Group + "/" + d.Project
- }
-
- if d.Group != "" {
- return d.Group
- }
-
- return d.Project
+ return d.Name
}
func (d *Domain) isCustomDomain() bool {
@@ -54,7 +38,7 @@ func (d *Domain) isCustomDomain() bool {
panic("project config and group config should not be nil at the same time")
}
- return d.ProjectConfig != nil && d.GroupConfig == nil
+ return d.Customized
}
func (d *Domain) isUnconfigured() bool {
@@ -62,22 +46,52 @@ func (d *Domain) isUnconfigured() bool {
return true
}
- return d.ProjectConfig == nil && d.GroupConfig == nil
+ return d.Resolver == nil
+}
+
+func (d *Domain) resolve(r *http.Request) (*Project, string) {
+ // TODO use lookupPaths first
+
+ project, subpath, _ := d.Resolver.Resolve(r)
+ // current implementation does not return errors in any case
+
+ if project == nil {
+ return nil, ""
+ }
+
+ return project, subpath
+}
+
+func (d *Domain) getProject(r *http.Request) *Project {
+ project, _ := d.resolve(r)
+
+ return project
}
// Serving returns domain serving driver
func (d *Domain) Serving() serving.Serving {
if d.serving == nil {
- if d.isCustomDomain() {
- d.serving = serving.NewProjectDiskServing(d.Project, d.Group)
- } else {
- d.serving = serving.NewGroupDiskServing(d.Group, d.GroupConfig)
- }
+ d.serving = serving.NewDiskServing(d.Name, d.Location)
}
return d.serving
}
+func (d *Domain) toHandler(w http.ResponseWriter, r *http.Request) *handler {
+ project, subpath := d.resolve(r)
+
+ return &handler{
+ writer: w,
+ request: r,
+ project: project,
+ subpath: subpath,
+ }
+}
+
+func (d *Domain) hasProject(r *http.Request) bool {
+ return d.getProject(r) != nil
+}
+
// IsHTTPSOnly figures out if the request should be handled with HTTPS
// only by looking at group and project level config.
func (d *Domain) IsHTTPSOnly(r *http.Request) bool {
@@ -85,13 +99,11 @@ func (d *Domain) IsHTTPSOnly(r *http.Request) bool {
return false
}
- // Check custom domain config (e.g. http://example.com)
- if d.isCustomDomain() {
- return d.ProjectConfig.HTTPSOnly
+ if project := d.getProject(r); project != nil {
+ return project.IsHTTPSOnly
}
- // Check projects served under the group domain, including the default one
- return d.GroupConfig.IsHTTPSOnly(r)
+ return false
}
// IsAccessControlEnabled figures out if the request is to a project that has access control enabled
@@ -100,22 +112,20 @@ func (d *Domain) IsAccessControlEnabled(r *http.Request) bool {
return false
}
- // Check custom domain config (e.g. http://example.com)
- if d.isCustomDomain() {
- return d.ProjectConfig.AccessControl
+ if project := d.getProject(r); project != nil {
+ return project.HasAccessControl
}
- // Check projects served under the group domain, including the default one
- return d.GroupConfig.HasAccessControl(r)
+ return false
}
// HasAcmeChallenge checks domain directory contains particular acme challenge
-func (d *Domain) HasAcmeChallenge(token string) bool {
- if d.isUnconfigured() || !d.isCustomDomain() {
+func (d *Domain) HasAcmeChallenge(r *http.Request, token string) bool {
+ if d.isUnconfigured() || !d.isCustomDomain() || !d.hasProject(r) {
return false
}
- return d.Serving().HasAcmeChallenge(token)
+ return d.Serving().HasAcmeChallenge(d.toHandler(nil, r), token) // TODO
}
// IsNamespaceProject figures out if the request is to a namespace project
@@ -126,12 +136,15 @@ func (d *Domain) IsNamespaceProject(r *http.Request) bool {
// If request is to a custom domain, we do not handle it as a namespace project
// as there can't be multiple projects under the same custom domain
- if d.isCustomDomain() {
+ if d.isCustomDomain() { // TODO do we need a separate path for this
return false
}
- // Check projects served under the group domain, including the default one
- return d.GroupConfig.IsNamespaceProject(r)
+ if project := d.getProject(r); project != nil {
+ return project.IsNamespaceProject
+ }
+
+ return false
}
// GetID figures out what is the ID of the project user tries to access
@@ -140,11 +153,11 @@ func (d *Domain) GetID(r *http.Request) uint64 {
return 0
}
- if d.isCustomDomain() {
- return d.ProjectConfig.ProjectID
+ if project := d.getProject(r); project != nil {
+ return project.ID
}
- return d.GroupConfig.ProjectID(r)
+ return 0
}
// HasProject figures out if the project exists that the user tries to access
@@ -153,15 +166,16 @@ func (d *Domain) HasProject(r *http.Request) bool {
return false
}
- if d.isCustomDomain() {
+ if project := d.getProject(r); project != nil {
return true
}
- return d.GroupConfig.ProjectExists(r)
+ return false
}
// EnsureCertificate parses the PEM-encoded certificate for the domain
func (d *Domain) EnsureCertificate() (*tls.Certificate, error) {
+ // TODO check len certificates instead of custom domain!
if d.isUnconfigured() || !d.isCustomDomain() {
return nil, errors.New("tls certificates can be loaded only for pages with configuration")
}
@@ -169,8 +183,8 @@ func (d *Domain) EnsureCertificate() (*tls.Certificate, error) {
d.certificateOnce.Do(func() {
var cert tls.Certificate
cert, d.certificateError = tls.X509KeyPair(
- []byte(d.ProjectConfig.Certificate),
- []byte(d.ProjectConfig.Key),
+ []byte(d.CertificateCert),
+ []byte(d.CertificateKey),
)
if d.certificateError == nil {
d.certificate = &cert
@@ -182,26 +196,20 @@ func (d *Domain) EnsureCertificate() (*tls.Certificate, error) {
// ServeFileHTTP returns true if something was served, false if not.
func (d *Domain) ServeFileHTTP(w http.ResponseWriter, r *http.Request) bool {
- if d.isUnconfigured() {
+ if d.isUnconfigured() || !d.hasProject(r) {
httperrors.Serve404(w)
return true
}
- if !d.IsAccessControlEnabled(r) {
- // Set caching headers
- w.Header().Set("Cache-Control", "max-age=600")
- w.Header().Set("Expires", time.Now().Add(10*time.Minute).Format(time.RFC1123))
- }
-
- return d.Serving().ServeFileHTTP(w, r)
+ return d.Serving().ServeFileHTTP(d.toHandler(w, r))
}
// ServeNotFoundHTTP serves the not found pages from the projects.
func (d *Domain) ServeNotFoundHTTP(w http.ResponseWriter, r *http.Request) {
- if d.isUnconfigured() {
+ if d.isUnconfigured() || !d.hasProject(r) {
httperrors.Serve404(w)
return
}
- d.Serving().ServeNotFoundHTTP(w, r)
+ d.Serving().ServeNotFoundHTTP(d.toHandler(w, r))
}
diff --git a/internal/domain/domain_test.go b/internal/domain/domain_test.go
index 2be86e20..8b369075 100644
--- a/internal/domain/domain_test.go
+++ b/internal/domain/domain_test.go
@@ -11,10 +11,19 @@ import (
"github.com/stretchr/testify/require"
- "gitlab.com/gitlab-org/gitlab-pages/internal/fixture"
"gitlab.com/gitlab-org/gitlab-pages/internal/testhelpers"
)
+type stubbedResolver struct {
+ project *Project
+ subpath string
+ err error
+}
+
+func (resolver *stubbedResolver) Resolve(*http.Request) (*Project, string, error) {
+ return resolver.project, resolver.subpath, resolver.err
+}
+
func serveFileOrNotFound(domain *Domain) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !domain.ServeFileHTTP(w, r) {
@@ -23,27 +32,6 @@ func serveFileOrNotFound(domain *Domain) http.HandlerFunc {
}
}
-func TestDomainServeHTTP(t *testing.T) {
- cleanup := setUpTests(t)
- defer cleanup()
-
- testDomain := &Domain{
- Project: "project2",
- Group: "group",
- ProjectConfig: &ProjectConfig{DomainName: "test.domain.com"},
- }
-
- require.HTTPBodyContains(t, serveFileOrNotFound(testDomain), "GET", "/", nil, "project2-main")
- require.HTTPBodyContains(t, serveFileOrNotFound(testDomain), "GET", "/index.html", nil, "project2-main")
- require.HTTPRedirect(t, serveFileOrNotFound(testDomain), "GET", "/subdir", nil)
- require.HTTPBodyContains(t, serveFileOrNotFound(testDomain), "GET", "/subdir", nil,
- `<a href="/subdir/">Found</a>`)
- require.HTTPBodyContains(t, serveFileOrNotFound(testDomain), "GET", "/subdir/", nil, "project2-subdir")
- require.HTTPBodyContains(t, serveFileOrNotFound(testDomain), "GET", "/subdir/index.html", nil, "project2-subdir")
- require.HTTPError(t, serveFileOrNotFound(testDomain), "GET", "//about.gitlab.com/%2e%2e", nil)
- require.HTTPError(t, serveFileOrNotFound(testDomain), "GET", "/not-existing-file", nil)
-}
-
func TestIsHTTPSOnly(t *testing.T) {
tests := []struct {
name string
@@ -54,9 +42,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Custom domain with HTTPS-only enabled",
domain: &Domain{
- Project: "project",
- Group: "group",
- ProjectConfig: &ProjectConfig{HTTPSOnly: true},
+ Location: "group/project",
+ Resolver: &stubbedResolver{project: &Project{IsHTTPSOnly: true}},
},
url: "http://custom-domain",
expected: true,
@@ -64,9 +51,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Custom domain with HTTPS-only disabled",
domain: &Domain{
- Project: "project",
- Group: "group",
- ProjectConfig: &ProjectConfig{HTTPSOnly: false},
+ Location: "group/project",
+ Resolver: &stubbedResolver{project: &Project{IsHTTPSOnly: false}},
},
url: "http://custom-domain",
expected: false,
@@ -74,8 +60,7 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Unknown project",
domain: &Domain{
- Project: "project",
- Group: "group",
+ Location: "group/project",
},
url: "http://test-domain/project",
expected: false,
@@ -90,69 +75,6 @@ func TestIsHTTPSOnly(t *testing.T) {
}
}
-func TestHasAcmeChallenge(t *testing.T) {
- cleanup := setUpTests(t)
- defer cleanup()
-
- tests := []struct {
- name string
- domain *Domain
- token string
- expected bool
- }{
- {
- name: "Project containing acme challenge",
- domain: &Domain{
- Group: "group.acme",
- Project: "with.acme.challenge",
- ProjectConfig: &ProjectConfig{HTTPSOnly: true},
- },
- token: "existingtoken",
- expected: true,
- },
- {
- name: "Project containing acme challenge",
- domain: &Domain{
- Group: "group.acme",
- Project: "with.acme.challenge",
- ProjectConfig: &ProjectConfig{HTTPSOnly: true},
- },
- token: "foldertoken",
- expected: true,
- },
- {
- name: "Project containing another token",
- domain: &Domain{
- Group: "group.acme",
- Project: "with.acme.challenge",
- ProjectConfig: &ProjectConfig{HTTPSOnly: true},
- },
- token: "notexistingtoken",
- expected: false,
- },
- {
- name: "nil domain",
- domain: nil,
- token: "existingtoken",
- expected: false,
- },
- {
- name: "Domain without config",
- domain: &Domain{
- Group: "group.acme",
- Project: "with.acme.challenge",
- },
- token: "existingtoken",
- expected: false,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- require.Equal(t, test.expected, test.domain.HasAcmeChallenge(test.token))
- })
- }
-}
-
func testHTTPGzip(t *testing.T, handler http.HandlerFunc, mode, url string, values url.Values, acceptEncoding string, str interface{}, contentType string, ungzip bool) {
w := httptest.NewRecorder()
req, err := http.NewRequest(mode, url+"?"+values.Encode(), nil)
@@ -180,26 +102,12 @@ func testHTTPGzip(t *testing.T, handler http.HandlerFunc, mode, url string, valu
require.Equal(t, contentType, w.Header().Get("Content-Type"))
}
-func TestDomain404ServeHTTP(t *testing.T) {
- cleanup := setUpTests(t)
- defer cleanup()
-
- testDomain := &Domain{
- Group: "group.404",
- Project: "domain.404",
- ProjectConfig: &ProjectConfig{DomainName: "domain.404.com"},
- }
-
- testhelpers.AssertHTTP404(t, serveFileOrNotFound(testDomain), "GET", "http://group.404.test.io/not-existing-file", nil, "Custom 404 group page")
- testhelpers.AssertHTTP404(t, serveFileOrNotFound(testDomain), "GET", "http://group.404.test.io/", nil, "Custom 404 group page")
-}
-
func TestPredefined404ServeHTTP(t *testing.T) {
cleanup := setUpTests(t)
defer cleanup()
testDomain := &Domain{
- Group: "group",
+ Location: "group",
}
testhelpers.AssertHTTP404(t, serveFileOrNotFound(testDomain), "GET", "http://group.test.io/not-existing-file", nil, "The page you're looking for could not be found")
@@ -207,8 +115,7 @@ func TestPredefined404ServeHTTP(t *testing.T) {
func TestGroupCertificate(t *testing.T) {
testGroup := &Domain{
- Project: "",
- Group: "group",
+ Location: "group",
}
tls, err := testGroup.EnsureCertificate()
@@ -218,9 +125,8 @@ func TestGroupCertificate(t *testing.T) {
func TestDomainNoCertificate(t *testing.T) {
testDomain := &Domain{
- Group: "group",
- Project: "project2",
- ProjectConfig: &ProjectConfig{DomainName: "test.domain.com"},
+ Name: "test.domain.com",
+ Location: "group/project2",
}
tls, err := testDomain.EnsureCertificate()
@@ -232,22 +138,6 @@ func TestDomainNoCertificate(t *testing.T) {
require.Equal(t, err, err2)
}
-func TestDomainCertificate(t *testing.T) {
- testDomain := &Domain{
- Group: "group",
- Project: "project2",
- ProjectConfig: &ProjectConfig{
- DomainName: "test.domain.com",
- Certificate: fixture.Certificate,
- Key: fixture.Key,
- },
- }
-
- tls, err := testDomain.EnsureCertificate()
- require.NotNil(t, tls)
- require.NoError(t, err)
-}
-
var chdirSet = false
func setUpTests(t require.TestingT) func() {
diff --git a/internal/domain/handler.go b/internal/domain/handler.go
new file mode 100644
index 00000000..55697700
--- /dev/null
+++ b/internal/domain/handler.go
@@ -0,0 +1,42 @@
+package domain
+
+import "net/http"
+
+type handler struct {
+ writer http.ResponseWriter
+ request *http.Request
+ project *Project
+ subpath string
+}
+
+func (h *handler) Writer() http.ResponseWriter {
+ return h.writer
+}
+
+func (h *handler) Request() *http.Request {
+ return h.request
+}
+
+func (h *handler) LookupPath() string {
+ return h.project.LookupPath
+}
+
+func (h *handler) Subpath() string {
+ return h.subpath
+}
+
+func (h *handler) IsNamespaceProject() bool {
+ return h.project.IsNamespaceProject
+}
+
+func (h *handler) IsHTTPSOnly() bool {
+ return h.project.IsHTTPSOnly
+}
+
+func (h *handler) HasAccessControl() bool {
+ return h.project.HasAccessControl
+}
+
+func (h *handler) ProjectID() uint64 {
+ return h.project.ID
+}
diff --git a/internal/domain/project.go b/internal/domain/project.go
new file mode 100644
index 00000000..9ea7306f
--- /dev/null
+++ b/internal/domain/project.go
@@ -0,0 +1,10 @@
+package domain
+
+// Project holds a domain / project configuration
+type Project struct {
+ LookupPath string
+ IsNamespaceProject bool
+ IsHTTPSOnly bool
+ HasAccessControl bool
+ ID uint64
+}
diff --git a/internal/domain/resolver.go b/internal/domain/resolver.go
new file mode 100644
index 00000000..5bde31ec
--- /dev/null
+++ b/internal/domain/resolver.go
@@ -0,0 +1,10 @@
+package domain
+
+import "net/http"
+
+// Resolver represents an interface responsible for resolving a project
+// per-request
+type Resolver interface {
+ // Resolve returns a project with a file path and an error if it occured
+ Resolve(*http.Request) (*Project, string, error)
+}
diff --git a/internal/serving/disk.go b/internal/serving/disk.go
new file mode 100644
index 00000000..b569986f
--- /dev/null
+++ b/internal/serving/disk.go
@@ -0,0 +1,19 @@
+package serving
+
+import "gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk"
+
+type diskServing struct {
+ disk *disk.Serving
+}
+
+func (d *diskServing) ServeFileHTTP(h Handler) bool {
+ return d.disk.ServeFileHTTP(h)
+}
+
+func (d *diskServing) ServeNotFoundHTTP(h Handler) {
+ d.disk.ServeNotFoundHTTP(h)
+}
+
+func (d *diskServing) HasAcmeChallenge(h Handler, token string) bool {
+ return d.disk.HasAcmeChallenge(h, token)
+}
diff --git a/internal/serving/disk/group.go b/internal/serving/disk/group.go
deleted file mode 100644
index 78a4ff1f..00000000
--- a/internal/serving/disk/group.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package disk
-
-import (
- "net/http"
-
- "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors"
-)
-
-// Group serving represents a resource that can be served from a directory
-// representing GitLab group
-type Group struct {
- Resolver
- *Reader
-}
-
-type Resolver interface {
- ProjectWithSubpath(*http.Request) (string, string, error)
-}
-
-// ServeFileHTTP returns true if something was served, false if not.
-func (g *Group) ServeFileHTTP(w http.ResponseWriter, r *http.Request) bool {
- return g.serveFileFromGroup(w, r)
-}
-
-// ServeNotFoundHTTP serves the not found pages from the projects.
-func (g *Group) ServeNotFoundHTTP(w http.ResponseWriter, r *http.Request) {
- g.serveNotFoundFromGroup(w, r)
-}
-
-func (g *Group) HasAcmeChallenge(token string) bool {
- return false
-}
-
-func (g *Group) serveFileFromGroup(w http.ResponseWriter, r *http.Request) bool {
- projectName, subPath, err := g.Resolver.ProjectWithSubpath(r)
-
- if err != nil {
- httperrors.Serve404(w)
- return true
- }
-
- if g.tryFile(w, r, projectName, subPath) == nil {
- return true
- }
-
- return false
-}
-
-func (g *Group) serveNotFoundFromGroup(w http.ResponseWriter, r *http.Request) {
- projectName, _, err := g.Resolver.ProjectWithSubpath(r)
-
- if err != nil {
- httperrors.Serve404(w)
- return
- }
-
- // Try serving custom not-found page
- if g.tryNotFound(w, r, projectName) == nil {
- return
- }
-
- // Generic 404
- httperrors.Serve404(w)
-}
diff --git a/internal/serving/disk/handler.go b/internal/serving/disk/handler.go
new file mode 100644
index 00000000..fbbf9ce3
--- /dev/null
+++ b/internal/serving/disk/handler.go
@@ -0,0 +1,11 @@
+package disk
+
+import "net/http"
+
+type handler interface {
+ Writer() http.ResponseWriter
+ Request() *http.Request
+ LookupPath() string
+ Subpath() string
+ HasAccessControl() bool
+}
diff --git a/internal/serving/disk/project.go b/internal/serving/disk/project.go
deleted file mode 100644
index b2940f3d..00000000
--- a/internal/serving/disk/project.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package disk
-
-import (
- "net/http"
-
- "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors"
-)
-
-// Custom serving represent a resource that can be served from a directory
-// representing GitLab project
-type Project struct {
- Location string
- *Reader
-}
-
-// ServeFileHTTP returns true if something was served, false if not.
-func (p *Project) ServeFileHTTP(w http.ResponseWriter, r *http.Request) bool {
- return p.serveFileFromConfig(w, r)
-}
-
-// ServeNotFoundHTTP serves the not found pages from the projects.
-func (p *Project) ServeNotFoundHTTP(w http.ResponseWriter, r *http.Request) {
- p.serveNotFoundFromConfig(w, r)
-}
-
-func (p *Project) HasAcmeChallenge(token string) bool {
- _, err := p.resolvePath(p.Location, ".well-known/acme-challenge", token)
- // there is an acme challenge on disk
- if err == nil {
- return true
- }
-
- _, err = p.resolvePath(p.Location, ".well-known/acme-challenge", token, "index.html")
- if err == nil {
- return true
- }
-
- return false
-}
-
-func (p *Project) serveFileFromConfig(w http.ResponseWriter, r *http.Request) bool {
- // Try to serve file for http://host/... => /group/project/...
- if p.tryFile(w, r, p.Location, r.URL.Path) == nil {
- return true
- }
-
- return false
-}
-
-func (p *Project) serveNotFoundFromConfig(w http.ResponseWriter, r *http.Request) {
- // Try serving not found page for http://host/ => /group/project/404.html
- if p.tryNotFound(w, r, p.Location) == nil {
- return
- }
-
- // Serve generic not found
- httperrors.Serve404(w)
-}
diff --git a/internal/serving/disk/reader.go b/internal/serving/disk/reader.go
index 068b4b47..1fc30da7 100644
--- a/internal/serving/disk/reader.go
+++ b/internal/serving/disk/reader.go
@@ -8,48 +8,54 @@ import (
"path/filepath"
"strconv"
"strings"
+ "time"
)
+// Reader is a disk access driver
type Reader struct {
- Group string
+ Location string
}
-func (reader *Reader) tryFile(w http.ResponseWriter, r *http.Request, projectName string, subPath ...string) error {
- fullPath, err := reader.resolvePath(projectName, subPath...)
+func (reader *Reader) tryFile(h handler) error {
+ fullPath, err := reader.resolvePath(h.LookupPath(), h.Subpath())
+
+ request := h.Request()
+ host := request.Host
+ urlPath := request.URL.Path
if locationError, _ := err.(*locationDirectoryError); locationError != nil {
- if endsWithSlash(r.URL.Path) {
- fullPath, err = reader.resolvePath(projectName, filepath.Join(subPath...), "index.html")
+ if endsWithSlash(urlPath) {
+ fullPath, err = reader.resolvePath(h.LookupPath(), h.Subpath(), "index.html")
} else {
// Concat Host with URL.Path
- redirectPath := "//" + r.Host + "/"
- redirectPath += strings.TrimPrefix(r.URL.Path, "/")
+ redirectPath := "//" + host + "/"
+ redirectPath += strings.TrimPrefix(urlPath, "/")
// Ensure that there's always "/" at end
redirectPath = strings.TrimSuffix(redirectPath, "/") + "/"
- http.Redirect(w, r, redirectPath, 302)
+ http.Redirect(h.Writer(), h.Request(), redirectPath, 302)
return nil
}
}
if locationError, _ := err.(*locationFileNoExtensionError); locationError != nil {
- fullPath, err = reader.resolvePath(projectName, strings.TrimSuffix(filepath.Join(subPath...), "/")+".html")
+ fullPath, err = reader.resolvePath(h.LookupPath(), strings.TrimSuffix(h.Subpath(), "/")+".html")
}
if err != nil {
return err
}
- return reader.serveFile(w, r, fullPath)
+ return reader.serveFile(h.Writer(), h.Request(), fullPath, h.HasAccessControl())
}
-func (reader *Reader) tryNotFound(w http.ResponseWriter, r *http.Request, projectName string) error {
- page404, err := reader.resolvePath(projectName, "404.html")
+func (reader *Reader) tryNotFound(h handler) error {
+ page404, err := reader.resolvePath(h.LookupPath(), "404.html")
if err != nil {
return err
}
- err = reader.serveCustomFile(w, r, http.StatusNotFound, page404)
+ err = reader.serveCustomFile(h.Writer(), h.Request(), http.StatusNotFound, page404)
if err != nil {
return err
}
@@ -58,8 +64,8 @@ func (reader *Reader) tryNotFound(w http.ResponseWriter, r *http.Request, projec
// Resolve the HTTP request to a path on disk, converting requests for
// directories to requests for index.html inside the directory if appropriate.
-func (reader *Reader) resolvePath(projectName string, subPath ...string) (string, error) {
- publicPath := filepath.Join(reader.Group, projectName, "public")
+func (reader *Reader) resolvePath(lookupPath string, subPath ...string) (string, error) {
+ publicPath := filepath.Join(reader.Location, lookupPath, "public")
// Don't use filepath.Join as cleans the path,
// where we want to traverse full path as supplied by user
@@ -103,7 +109,7 @@ func (reader *Reader) resolvePath(projectName string, subPath ...string) (string
return fullPath, nil
}
-func (reader *Reader) serveFile(w http.ResponseWriter, r *http.Request, origPath string) error {
+func (reader *Reader) serveFile(w http.ResponseWriter, r *http.Request, origPath string, accessControl bool) error {
fullPath := handleGZip(w, r, origPath)
file, err := openNoFollow(fullPath)
@@ -118,6 +124,12 @@ func (reader *Reader) serveFile(w http.ResponseWriter, r *http.Request, origPath
return err
}
+ if !accessControl {
+ // Set caching headers
+ w.Header().Set("Cache-Control", "max-age=600")
+ w.Header().Set("Expires", time.Now().Add(10*time.Minute).Format(time.RFC1123))
+ }
+
contentType, err := detectContentType(origPath)
if err != nil {
return err
diff --git a/internal/serving/disk/serving.go b/internal/serving/disk/serving.go
new file mode 100644
index 00000000..78b1b572
--- /dev/null
+++ b/internal/serving/disk/serving.go
@@ -0,0 +1,47 @@
+package disk
+
+import (
+ "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors"
+)
+
+// Serving describes a disk access serving
+type Serving struct {
+ Domain string // TODO it is not used but might be handy
+ *Reader
+}
+
+// ServeFileHTTP serves a file from disk and returns true. It returns false
+// when a file could not been found.
+func (s *Serving) ServeFileHTTP(h handler) bool {
+ if s.tryFile(h) == nil {
+ return true
+ }
+
+ return false
+}
+
+// ServeNotFoundHTTP tries to read a custom 404 page
+func (s *Serving) ServeNotFoundHTTP(h handler) {
+ if s.tryNotFound(h) == nil {
+ return
+ }
+
+ // Generic 404
+ httperrors.Serve404(h.Writer())
+}
+
+// HasAcmeChallenge checks if the ACME challenge is present on the disk
+func (s *Serving) HasAcmeChallenge(h handler, token string) bool {
+ _, err := s.resolvePath(h.LookupPath(), ".well-known/acme-challenge", token)
+ // there is an acme challenge on disk
+ if err == nil {
+ return true
+ }
+
+ _, err = s.resolvePath(h.LookupPath(), ".well-known/acme-challenge", token, "index.html")
+ if err == nil {
+ return true
+ }
+
+ return false
+}
diff --git a/internal/serving/handler.go b/internal/serving/handler.go
new file mode 100644
index 00000000..2a9969b7
--- /dev/null
+++ b/internal/serving/handler.go
@@ -0,0 +1,16 @@
+package serving
+
+import "net/http"
+
+// Handler interface represent an interface that is needed to fullfil the
+// serving request
+type Handler interface {
+ Writer() http.ResponseWriter
+ Request() *http.Request
+ LookupPath() string
+ Subpath() string
+ IsNamespaceProject() bool
+ IsHTTPSOnly() bool
+ HasAccessControl() bool
+ ProjectID() uint64
+}
diff --git a/internal/serving/serving.go b/internal/serving/serving.go
index b28d0900..c6a39241 100644
--- a/internal/serving/serving.go
+++ b/internal/serving/serving.go
@@ -1,32 +1,23 @@
package serving
import (
- "net/http"
-
"gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk"
)
// Serving is an interface used to define a serving driver
type Serving interface {
- ServeFileHTTP(http.ResponseWriter, *http.Request) bool
- ServeNotFoundHTTP(http.ResponseWriter, *http.Request)
- HasAcmeChallenge(token string) bool
-}
-
-func NewProjectDiskServing(project, group string) Serving {
- return &disk.Project{
- Location: project,
- Reader: &disk.Reader{
- Group: group,
- },
- }
+ ServeFileHTTP(Handler) bool
+ ServeNotFoundHTTP(Handler)
+ HasAcmeChallenge(handler Handler, token string) bool
}
-func NewGroupDiskServing(group string, resolver disk.Resolver) Serving {
- return &disk.Group{
- Resolver: resolver,
- Reader: &disk.Reader{
- Group: group,
+// NewDiskServing returns a serving instance that is capable of reading files
+// from the disk
+func NewDiskServing(domain, location string) Serving {
+ return &diskServing{
+ disk: &disk.Serving{
+ Domain: domain,
+ Reader: &disk.Reader{Location: location},
},
}
}
diff --git a/internal/source/disk/custom.go b/internal/source/disk/custom.go
new file mode 100644
index 00000000..0cf443b0
--- /dev/null
+++ b/internal/source/disk/custom.go
@@ -0,0 +1,24 @@
+package disk
+
+import (
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-pages/internal/domain"
+)
+
+type customProjectResolver struct {
+ config *domainConfig
+}
+
+// TODO tests
+func (p *customProjectResolver) Resolve(r *http.Request) (*domain.Project, string, error) {
+ project := &domain.Project{
+ LookupPath: "/",
+ IsNamespaceProject: false,
+ IsHTTPSOnly: p.config.HTTPSOnly,
+ HasAccessControl: p.config.AccessControl,
+ ID: p.config.ID,
+ }
+
+ return project, r.URL.Path, nil
+}
diff --git a/internal/source/disk/group_domain_test.go b/internal/source/disk/domain_test.go
index 3b4471f4..80772d37 100644
--- a/internal/source/disk/group_domain_test.go
+++ b/internal/source/disk/domain_test.go
@@ -27,9 +27,8 @@ func serveFileOrNotFound(domain *domain.Domain) http.HandlerFunc {
func testGroupServeHTTPHost(t *testing.T, host string) {
testGroup := &domain.Domain{
- Project: "",
- Group: "group",
- GroupConfig: &Group{
+ Location: "group",
+ Resolver: &Group{
name: "group",
projects: map[string]*projectConfig{
"group.test.io": &projectConfig{},
@@ -82,9 +81,11 @@ func TestDomainServeHTTP(t *testing.T) {
defer cleanup()
testDomain := &domain.Domain{
- Group: "group",
- Project: "project2",
- ProjectConfig: &domain.ProjectConfig{DomainName: "test.domain.com"},
+ Name: "test.domain.com",
+ Location: "group/project2",
+ Resolver: &customProjectResolver{
+ config: &domainConfig{},
+ },
}
require.HTTPBodyContains(t, serveFileOrNotFound(testDomain), "GET", "/", nil, "project2-main")
@@ -108,9 +109,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Default group domain with HTTPS-only enabled",
domain: &domain.Domain{
- Group: "group",
- Project: "project",
- GroupConfig: &Group{
+ Location: "group/project",
+ Resolver: &Group{
name: "group",
projects: projects{"test-domain": &projectConfig{HTTPSOnly: true}},
},
@@ -121,9 +121,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Default group domain with HTTPS-only disabled",
domain: &domain.Domain{
- Group: "group",
- Project: "project",
- GroupConfig: &Group{
+ Location: "group/project",
+ Resolver: &Group{
name: "group",
projects: projects{"test-domain": &projectConfig{HTTPSOnly: false}},
},
@@ -134,9 +133,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Case-insensitive default group domain with HTTPS-only enabled",
domain: &domain.Domain{
- Project: "project",
- Group: "group",
- GroupConfig: &Group{
+ Location: "group/project",
+ Resolver: &Group{
name: "group",
projects: projects{"test-domain": &projectConfig{HTTPSOnly: true}},
},
@@ -147,9 +145,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Other group domain with HTTPS-only enabled",
domain: &domain.Domain{
- Project: "project",
- Group: "group",
- GroupConfig: &Group{
+ Location: "group/project",
+ Resolver: &Group{
name: "group",
projects: projects{"project": &projectConfig{HTTPSOnly: true}},
},
@@ -160,9 +157,8 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Other group domain with HTTPS-only disabled",
domain: &domain.Domain{
- Project: "project",
- Group: "group",
- GroupConfig: &Group{
+ Location: "group/project",
+ Resolver: &Group{
name: "group",
projects: projects{"project": &projectConfig{HTTPSOnly: false}},
},
@@ -173,8 +169,7 @@ func TestIsHTTPSOnly(t *testing.T) {
{
name: "Unknown project",
domain: &domain.Domain{
- Group: "group",
- Project: "project",
+ Location: "group/project",
},
url: "http://test-domain/project",
expected: false,
@@ -221,9 +216,8 @@ func TestGroupServeHTTPGzip(t *testing.T) {
defer cleanup()
testGroup := &domain.Domain{
- Project: "",
- Group: "group",
- GroupConfig: &Group{
+ Location: "group",
+ Resolver: &Group{
name: "group",
projects: map[string]*projectConfig{
"group.test.io": &projectConfig{},
@@ -289,9 +283,8 @@ func TestGroup404ServeHTTP(t *testing.T) {
defer cleanup()
testGroup := &domain.Domain{
- Project: "",
- Group: "group.404",
- GroupConfig: &Group{
+ Location: "group.404",
+ Resolver: &Group{
name: "group.404",
projects: map[string]*projectConfig{
"domain.404": &projectConfig{},
@@ -319,9 +312,10 @@ func TestDomain404ServeHTTP(t *testing.T) {
defer cleanup()
testDomain := &domain.Domain{
- Project: "domain.404",
- Group: "group.404",
- ProjectConfig: &domain.ProjectConfig{DomainName: "domain.404.com"},
+ Location: "group.404/domain.404",
+ Resolver: &customProjectResolver{
+ config: &domainConfig{Domain: "domain.404.com"},
+ },
}
testhelpers.AssertHTTP404(t, serveFileOrNotFound(testDomain), "GET", "http://group.404.test.io/not-existing-file", nil, "Custom 404 group page")
@@ -333,7 +327,7 @@ func TestPredefined404ServeHTTP(t *testing.T) {
defer cleanup()
testDomain := &domain.Domain{
- Group: "group",
+ Location: "group",
}
testhelpers.AssertHTTP404(t, serveFileOrNotFound(testDomain), "GET", "http://group.test.io/not-existing-file", nil, "The page you're looking for could not be found")
@@ -341,8 +335,7 @@ func TestPredefined404ServeHTTP(t *testing.T) {
func TestGroupCertificate(t *testing.T) {
testGroup := &domain.Domain{
- Group: "group",
- Project: "",
+ Location: "group",
}
tls, err := testGroup.EnsureCertificate()
@@ -352,9 +345,10 @@ func TestGroupCertificate(t *testing.T) {
func TestDomainNoCertificate(t *testing.T) {
testDomain := &domain.Domain{
- Group: "group",
- Project: "project2",
- ProjectConfig: &domain.ProjectConfig{DomainName: "test.domain.com"},
+ Location: "group/project2",
+ Resolver: &customProjectResolver{
+ config: &domainConfig{Domain: "test.domain.com"},
+ },
}
tls, err := testDomain.EnsureCertificate()
@@ -368,12 +362,12 @@ func TestDomainNoCertificate(t *testing.T) {
func TestDomainCertificate(t *testing.T) {
testDomain := &domain.Domain{
- Project: "project2",
- Group: "group",
- ProjectConfig: &domain.ProjectConfig{DomainName: "test.domain.com",
- Certificate: fixture.Certificate,
- Key: fixture.Key,
- },
+ Customized: true, // TODO
+ Location: "group/project2",
+ Name: "test.domain.com",
+ CertificateCert: fixture.Certificate,
+ CertificateKey: fixture.Key,
+ Resolver: &customProjectResolver{},
}
tls, err := testDomain.EnsureCertificate()
@@ -386,8 +380,8 @@ func TestCacheControlHeaders(t *testing.T) {
defer cleanup()
testGroup := &domain.Domain{
- Group: "group",
- GroupConfig: &Group{
+ Location: "group",
+ Resolver: &Group{
name: "group",
projects: map[string]*projectConfig{
"group.test.io": &projectConfig{},
diff --git a/internal/source/disk/group.go b/internal/source/disk/group.go
index 0c8d0810..efa3fce5 100644
--- a/internal/source/disk/group.go
+++ b/internal/source/disk/group.go
@@ -1,11 +1,11 @@
package disk
import (
- "errors"
"net/http"
"path"
"strings"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/domain"
"gitlab.com/gitlab-org/gitlab-pages/internal/host"
)
@@ -74,72 +74,22 @@ func (g *Group) getProjectConfigWithSubpath(r *http.Request) (*projectConfig, st
return nil, "", ""
}
-// IsHTTPSOnly return true if project exists and has https-only setting
-// configured
-func (g *Group) IsHTTPSOnly(r *http.Request) bool {
- project, _, _ := g.getProjectConfigWithSubpath(r)
+// Resolve tries to find project and its config recursively for a given request
+// to a group domain
+func (g *Group) Resolve(r *http.Request) (*domain.Project, string, error) {
+ projectConfig, projectPath, subPath := g.getProjectConfigWithSubpath(r)
- if project != nil {
- return project.HTTPSOnly
+ if projectConfig == nil {
+ return nil, "", nil // it is not an error when project does not exist
}
- return false
-}
-
-// HasAccessControl returns true if a group project has access control setting
-// enabled
-func (g *Group) HasAccessControl(r *http.Request) bool {
- project, _, _ := g.getProjectConfigWithSubpath(r)
-
- if project != nil {
- return project.AccessControl
- }
-
- return false
-}
-
-// IsNamespaceProject return true if per-request config belongs to a namespace
-// project
-func (g *Group) IsNamespaceProject(r *http.Request) bool {
- project, _, _ := g.getProjectConfigWithSubpath(r)
-
- if project != nil {
- return project.NamespaceProject
- }
-
- return false
-}
-
-// ProjectID return a per-request group project ID
-func (g *Group) ProjectID(r *http.Request) uint64 {
- project, _, _ := g.getProjectConfigWithSubpath(r)
-
- if project != nil {
- return project.ID
- }
-
- return 0
-}
-
-// ProjectExists return true if project config has been found
-func (g *Group) ProjectExists(r *http.Request) bool {
- project, _, _ := g.getProjectConfigWithSubpath(r)
-
- if project != nil {
- return true
- }
-
- return false
-}
-
-// ProjectWithSubpath tries to find project and its config recursively for a
-// given request to a group domain
-func (g *Group) ProjectWithSubpath(r *http.Request) (string, string, error) {
- project, projectName, subPath := g.getProjectConfigWithSubpath(r)
-
- if project != nil {
- return projectName, subPath, nil
+ project := &domain.Project{
+ LookupPath: projectPath,
+ IsNamespaceProject: projectConfig.NamespaceProject,
+ IsHTTPSOnly: projectConfig.HTTPSOnly,
+ HasAccessControl: projectConfig.AccessControl,
+ ID: projectConfig.ID,
}
- return "", "", errors.New("project not found")
+ return project, subPath, nil
}
diff --git a/internal/source/disk/map.go b/internal/source/disk/map.go
index dd368501..c330a2ea 100644
--- a/internal/source/disk/map.go
+++ b/internal/source/disk/map.go
@@ -24,11 +24,9 @@ type domainsUpdater func(Map)
func (dm Map) updateDomainMap(domainName string, domain *domain.Domain) {
if old, ok := dm[domainName]; ok {
log.WithFields(log.Fields{
- "domain_name": domainName,
- "new_group": domain.Group,
- "new_project_name": domain.Project,
- "old_group": old.Group,
- "old_project_name": old.Project,
+ "domain_name": domainName,
+ "new_location": domain.Location,
+ "old_location": old.Location,
}).Error("Duplicate domain")
}
@@ -37,21 +35,15 @@ func (dm Map) updateDomainMap(domainName string, domain *domain.Domain) {
func (dm Map) addDomain(rootDomain, groupName, projectName string, config *domainConfig) {
newDomain := &domain.Domain{
- Group: groupName,
- Project: projectName,
- ProjectConfig: &domain.ProjectConfig{
- DomainName: config.Domain,
- Certificate: config.Certificate,
- Key: config.Key,
- HTTPSOnly: config.HTTPSOnly,
- ProjectID: config.ID,
- AccessControl: config.AccessControl,
- },
+ Name: strings.ToLower(config.Domain),
+ Customized: true, // TODO remove
+ CertificateCert: config.Certificate,
+ CertificateKey: config.Key,
+ Location: filepath.Join(groupName, projectName),
+ Resolver: &customProjectResolver{config: config},
}
- var domainName string
- domainName = strings.ToLower(config.Domain)
- dm.updateDomainMap(domainName, newDomain)
+ dm.updateDomainMap(newDomain.Name, newDomain)
}
func (dm Map) updateGroupDomain(rootDomain, groupName, projectPath string, httpsOnly bool, accessControl bool, id uint64) {
@@ -59,21 +51,23 @@ func (dm Map) updateGroupDomain(rootDomain, groupName, projectPath string, https
groupDomain := dm[domainName]
if groupDomain == nil {
- group := &Group{
+ groupResolver := &Group{
name: groupName,
projects: make(projects),
subgroups: make(subgroups),
}
groupDomain = &domain.Domain{
- Group: groupName,
- GroupConfig: group,
+ Name: domainName,
+ Customized: false, // TODO remove
+ Location: groupName,
+ Resolver: groupResolver,
}
}
split := strings.SplitN(strings.ToLower(projectPath), "/", maxProjectDepth)
projectName := split[len(split)-1]
- g := groupDomain.GroupConfig.(*Group)
+ g := groupDomain.Resolver.(*Group)
for i := 0; i < len(split)-1; i++ {
subgroupName := split[i]
diff --git a/internal/source/disk/map_test.go b/internal/source/disk/map_test.go
index 9aa96072..c15f29c6 100644
--- a/internal/source/disk/map_test.go
+++ b/internal/source/disk/map_test.go
@@ -68,15 +68,15 @@ func TestReadProjects(t *testing.T) {
}
// Check that multiple domains in the same project are recorded faithfully
- require.Equal(t, "test.domain.com", dm["test.domain.com"].ProjectConfig.DomainName)
- require.Equal(t, "other.domain.com", dm["other.domain.com"].ProjectConfig.DomainName)
- require.Equal(t, "test", dm["other.domain.com"].ProjectConfig.Certificate)
- require.Equal(t, "key", dm["other.domain.com"].ProjectConfig.Key)
+ require.Equal(t, "test.domain.com", dm["test.domain.com"].Name)
+ require.Equal(t, "other.domain.com", dm["other.domain.com"].Name)
+ require.Equal(t, "test", dm["other.domain.com"].CertificateCert)
+ require.Equal(t, "key", dm["other.domain.com"].CertificateKey)
// check subgroups
domain, ok := dm["group.test.io"]
require.True(t, ok, "missing group.test.io domain")
- subgroup, ok := domain.GroupConfig.(*Group).subgroups["subgroup"]
+ subgroup, ok := domain.Resolver.(*Group).subgroups["subgroup"]
require.True(t, ok, "missing group.test.io subgroup")
_, ok = subgroup.projects["project"]
require.True(t, ok, "missing project for subgroup in group.test.io domain")
@@ -117,7 +117,7 @@ func TestReadProjectsMaxDepth(t *testing.T) {
// check subgroups
domain, ok := dm["group-0.test.io"]
require.True(t, ok, "missing group-0.test.io domain")
- subgroup := domain.GroupConfig.(*Group)
+ subgroup := domain.Resolver.(*Group)
for i := 0; i < levels; i++ {
subgroup, ok = subgroup.subgroups["sub"]
if i <= subgroupScanLimit {