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:
Diffstat (limited to 'internal/source')
-rw-r--r--internal/source/disk/custom.go9
-rw-r--r--internal/source/disk/group.go13
-rw-r--r--internal/source/domains.go27
-rw-r--r--internal/source/domains_test.go31
-rw-r--r--internal/source/gitlab/api/lookup_path.go27
-rw-r--r--internal/source/gitlab/factory.go51
-rw-r--r--internal/source/gitlab/factory_test.go64
-rw-r--r--internal/source/gitlab/gitlab.go31
-rw-r--r--internal/source/gitlab/gitlab_test.go46
9 files changed, 251 insertions, 48 deletions
diff --git a/internal/source/disk/custom.go b/internal/source/disk/custom.go
index cc4f3f4c..2668ed81 100644
--- a/internal/source/disk/custom.go
+++ b/internal/source/disk/custom.go
@@ -4,6 +4,7 @@ import (
"net/http"
"gitlab.com/gitlab-org/gitlab-pages/internal/serving"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk"
)
type customProjectResolver struct {
@@ -12,7 +13,7 @@ type customProjectResolver struct {
path string
}
-func (p *customProjectResolver) Resolve(r *http.Request) (*serving.LookupPath, string, error) {
+func (p *customProjectResolver) Resolve(r *http.Request) (*serving.Request, error) {
lookupPath := &serving.LookupPath{
Prefix: "/",
Path: p.path,
@@ -22,5 +23,9 @@ func (p *customProjectResolver) Resolve(r *http.Request) (*serving.LookupPath, s
ProjectID: p.config.ID,
}
- return lookupPath, r.URL.Path, nil
+ return &serving.Request{
+ Serving: disk.New(),
+ LookupPath: lookupPath,
+ SubPath: r.URL.Path,
+ }, nil
}
diff --git a/internal/source/disk/group.go b/internal/source/disk/group.go
index 9f466bc4..e0365bbd 100644
--- a/internal/source/disk/group.go
+++ b/internal/source/disk/group.go
@@ -8,6 +8,7 @@ import (
"gitlab.com/gitlab-org/gitlab-pages/internal/host"
"gitlab.com/gitlab-org/gitlab-pages/internal/serving"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk"
)
const (
@@ -77,11 +78,13 @@ func (g *Group) getProjectConfigWithSubpath(r *http.Request) (*projectConfig, st
// Resolve tries to find project and its config recursively for a given request
// to a group domain
-func (g *Group) Resolve(r *http.Request) (*serving.LookupPath, string, error) {
+func (g *Group) Resolve(r *http.Request) (*serving.Request, error) {
projectConfig, prefix, projectPath, subPath := g.getProjectConfigWithSubpath(r)
if projectConfig == nil {
- return nil, "", nil // it is not an error when project does not exist
+ // it is not an error when project does not exist, in that case
+ // serving.Request.LookupPath is nil.
+ return &serving.Request{Serving: disk.New()}, nil
}
lookupPath := &serving.LookupPath{
@@ -93,5 +96,9 @@ func (g *Group) Resolve(r *http.Request) (*serving.LookupPath, string, error) {
ProjectID: projectConfig.ID,
}
- return lookupPath, subPath, nil
+ return &serving.Request{
+ Serving: disk.New(),
+ LookupPath: lookupPath,
+ SubPath: subPath,
+ }, nil
}
diff --git a/internal/source/domains.go b/internal/source/domains.go
index 79357766..11794b91 100644
--- a/internal/source/domains.go
+++ b/internal/source/domains.go
@@ -2,6 +2,7 @@ package source
import (
"errors"
+ "regexp"
"time"
log "github.com/sirupsen/logrus"
@@ -13,7 +14,14 @@ import (
"gitlab.com/gitlab-org/gitlab-pages/internal/source/gitlab"
)
-var gitlabSourceConfig gitlabsourceconfig.GitlabSourceConfig
+var (
+ gitlabSourceConfig gitlabsourceconfig.GitlabSourceConfig
+
+ // serverlessDomainRegex is a regular expression we use to check if a domain
+ // is a serverless domain, to short circut gitlab source rollout. It can be
+ // removed after the rollout is done
+ serverlessDomainRegex = regexp.MustCompile(`^[^.]+-[[:xdigit:]]{2}a1[[:xdigit:]]{10}f2[[:xdigit:]]{2}[[:xdigit:]]+-?.*`)
+)
func init() {
// Start watching the config file for domains that will use the new `gitlab` source,
@@ -78,6 +86,13 @@ func (d *Domains) source(domain string) Source {
return d.disk
}
+ // This check is only needed until we enable `d.gitlab` source in all
+ // environments (including on-premises installations) followed by removal of
+ // `d.disk` source. This can be safely removed afterwards.
+ if IsServerlessDomain(domain) {
+ return d.gitlab
+ }
+
for _, name := range gitlabSourceConfig.Domains.Enabled {
if domain == name {
return d.gitlab
@@ -98,3 +113,13 @@ func (d *Domains) source(domain string) Source {
return d.disk
}
+
+// IsServerlessDomain checks if a domain requested is a serverless domain we
+// need to handle differently.
+//
+// Domain is a serverless domain when it matches `serverlessDomainRegex`. The
+// regular expression is also defined on the gitlab-rails side, see
+// https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/serverless/domain.rb#L7
+func IsServerlessDomain(domain string) bool {
+ return serverlessDomainRegex.MatchString(domain)
+}
diff --git a/internal/source/domains_test.go b/internal/source/domains_test.go
index d639b02d..9afde412 100644
--- a/internal/source/domains_test.go
+++ b/internal/source/domains_test.go
@@ -109,6 +109,37 @@ func TestGetDomain(t *testing.T) {
require.Nil(t, domain)
require.NoError(t, err)
})
+
+ t.Run("when requesting a serverless domain", func(t *testing.T) {
+ testDomain := "func-aba1aabbccddeef2abaabbcc.serverless.gitlab.io"
+
+ newSource := NewMockSource()
+ newSource.On("GetDomain", testDomain).
+ Return(&domain.Domain{Name: testDomain}, nil).
+ Once()
+ defer newSource.AssertExpectations(t)
+
+ domains := &Domains{
+ disk: disk.New(),
+ gitlab: newSource,
+ }
+
+ domains.GetDomain(testDomain)
+ })
+}
+
+func TestIsServerlessDomain(t *testing.T) {
+ t.Run("when a domain is serverless domain", func(t *testing.T) {
+ require.True(t, IsServerlessDomain("some-function-aba1aabbccddeef2abaabbcc.serverless.gitlab.io"))
+ })
+
+ t.Run("when a domain is serverless domain with environment", func(t *testing.T) {
+ require.True(t, IsServerlessDomain("some-function-aba1aabbccddeef2abaabbcc-testing.serverless.gitlab.io"))
+ })
+
+ t.Run("when a domain is not a serverless domain", func(t *testing.T) {
+ require.False(t, IsServerlessDomain("somedomain.gitlab.io"))
+ })
}
func TestGetDomainWithIncrementalrolloutOfGitLabSource(t *testing.T) {
diff --git a/internal/source/gitlab/api/lookup_path.go b/internal/source/gitlab/api/lookup_path.go
index b0407638..77b264ff 100644
--- a/internal/source/gitlab/api/lookup_path.go
+++ b/internal/source/gitlab/api/lookup_path.go
@@ -6,8 +6,27 @@ type LookupPath struct {
AccessControl bool `json:"access_control,omitempty"`
HTTPSOnly bool `json:"https_only,omitempty"`
Prefix string `json:"prefix,omitempty"`
- Source struct {
- Type string `json:"type,omitempty"`
- Path string `json:"path,omitempty"`
- }
+ Source Source `json:"source,omitempty"`
+}
+
+// Source describes GitLab Page serving variant
+type Source struct {
+ Type string `json:"type,omitempty"`
+ Path string `json:"path,omitempty"`
+ Serverless Serverless `json:"serverless,omitempty"`
+}
+
+// Serverless describes serverless serving configuration
+type Serverless struct {
+ Service string `json:"service,omitempty"`
+ Cluster Cluster `json:"cluster,omitempty"`
+}
+
+// Cluster describes serverless cluster configuration
+type Cluster struct {
+ Address string `json:"address,omitempty"`
+ Port string `json:"port,omitempty"`
+ Hostname string `json:"hostname,omitempty"`
+ CertificateCert string `json:"cert,omitempty"`
+ CertificateKey string `json:"key,omitempty"`
}
diff --git a/internal/source/gitlab/factory.go b/internal/source/gitlab/factory.go
new file mode 100644
index 00000000..d526994f
--- /dev/null
+++ b/internal/source/gitlab/factory.go
@@ -0,0 +1,51 @@
+package gitlab
+
+import (
+ "strings"
+
+ log "github.com/sirupsen/logrus"
+
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving/serverless"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/source/gitlab/api"
+)
+
+// fabricateLookupPath fabricates a serving LookupPath based on the API LookupPath
+// `size` argument is DEPRECATED, see
+// https://gitlab.com/gitlab-org/gitlab-pages/issues/272
+func fabricateLookupPath(size int, lookup api.LookupPath) *serving.LookupPath {
+ return &serving.LookupPath{
+ Prefix: lookup.Prefix,
+ Path: strings.TrimPrefix(lookup.Source.Path, "/"),
+ IsNamespaceProject: (lookup.Prefix == "/" && size > 1),
+ IsHTTPSOnly: lookup.HTTPSOnly,
+ HasAccessControl: lookup.AccessControl,
+ ProjectID: uint64(lookup.ProjectID),
+ }
+}
+
+// fabricateServing fabricates serving based on the GitLab API response
+func fabricateServing(lookup api.LookupPath) serving.Serving {
+ source := lookup.Source
+
+ switch source.Type {
+ case "file":
+ return disk.New()
+ case "serverless":
+ serving, err := serverless.NewFromAPISource(source.Serverless)
+ if err != nil {
+ log.WithError(err).Errorf("could not fabricate serving for project %d", lookup.ProjectID)
+
+ break
+ }
+
+ return serving
+ }
+
+ return defaultServing()
+}
+
+func defaultServing() serving.Serving {
+ return disk.New()
+}
diff --git a/internal/source/gitlab/factory_test.go b/internal/source/gitlab/factory_test.go
new file mode 100644
index 00000000..2f3e1994
--- /dev/null
+++ b/internal/source/gitlab/factory_test.go
@@ -0,0 +1,64 @@
+package gitlab
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-pages/internal/fixture"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving/disk"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/serving/serverless"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/source/gitlab/api"
+)
+
+func TestFabricateLookupPath(t *testing.T) {
+ t.Run("when lookup path is not a namespace project", func(t *testing.T) {
+ lookup := api.LookupPath{Prefix: "/something"}
+
+ path := fabricateLookupPath(1, lookup)
+
+ require.Equal(t, path.Prefix, "/something")
+ require.False(t, path.IsNamespaceProject)
+ })
+
+ t.Run("when lookup path is a namespace project", func(t *testing.T) {
+ lookup := api.LookupPath{Prefix: "/"}
+
+ path := fabricateLookupPath(2, lookup)
+
+ require.Equal(t, path.Prefix, "/")
+ require.True(t, path.IsNamespaceProject)
+ })
+}
+
+func TestFabricateServing(t *testing.T) {
+ t.Run("when lookup path requires disk serving", func(t *testing.T) {
+ lookup := api.LookupPath{
+ Prefix: "/",
+ Source: api.Source{Type: "file"},
+ }
+
+ require.IsType(t, &disk.Disk{}, fabricateServing(lookup))
+ })
+
+ t.Run("when lookup path requires serverless serving", func(t *testing.T) {
+ lookup := api.LookupPath{
+ Prefix: "/",
+ Source: api.Source{
+ Type: "serverless",
+ Serverless: api.Serverless{
+ Service: "my-func.knative.example.com",
+ Cluster: api.Cluster{
+ Address: "127.0.0.10",
+ Port: "443",
+ Hostname: "my-cluster.example.com",
+ CertificateCert: fixture.Certificate,
+ CertificateKey: fixture.Key,
+ },
+ },
+ },
+ }
+
+ require.IsType(t, &serverless.Serverless{}, fabricateServing(lookup))
+ })
+}
diff --git a/internal/source/gitlab/gitlab.go b/internal/source/gitlab/gitlab.go
index cce70733..6260200a 100644
--- a/internal/source/gitlab/gitlab.go
+++ b/internal/source/gitlab/gitlab.go
@@ -45,6 +45,8 @@ func (g *Gitlab) GetDomain(name string) (*domain.Domain, error) {
return nil, nil
}
+ // TODO introduce a second-level cache for domains, invalidate using etags
+ // from first-level cache
domain := domain.Domain{
Name: name,
CertificateCert: lookup.Domain.Certificate,
@@ -55,40 +57,39 @@ func (g *Gitlab) GetDomain(name string) (*domain.Domain, error) {
return &domain, nil
}
-// Resolve is supposed to get the serving lookup path based on the request from
-// the GitLab source
-func (g *Gitlab) Resolve(r *http.Request) (*serving.LookupPath, string, error) {
+// Resolve is supposed to return the serving request containing lookup path,
+// subpath for a given lookup and the serving itself created based on a request
+// from GitLab pages domains source
+func (g *Gitlab) Resolve(r *http.Request) (*serving.Request, error) {
host := request.GetHostWithoutPort(r)
response := g.client.Resolve(r.Context(), host)
if response.Error != nil {
- return nil, "", response.Error
+ return &serving.Request{Serving: defaultServing()}, response.Error
}
urlPath := path.Clean(r.URL.Path)
+ size := len(response.Domain.LookupPaths)
for _, lookup := range response.Domain.LookupPaths {
isSubPath := strings.HasPrefix(urlPath, lookup.Prefix)
isRootPath := urlPath == path.Clean(lookup.Prefix)
if isSubPath || isRootPath {
- lookupPath := &serving.LookupPath{
- Prefix: lookup.Prefix,
- Path: strings.TrimPrefix(lookup.Source.Path, "/"),
- IsNamespaceProject: (lookup.Prefix == "/" && len(response.Domain.LookupPaths) > 1),
- IsHTTPSOnly: lookup.HTTPSOnly,
- HasAccessControl: lookup.AccessControl,
- ProjectID: uint64(lookup.ProjectID),
- }
-
subPath := ""
if isSubPath {
subPath = strings.TrimPrefix(urlPath, lookup.Prefix)
}
- return lookupPath, subPath, nil
+ return &serving.Request{
+ Serving: fabricateServing(lookup),
+ LookupPath: fabricateLookupPath(size, lookup),
+ SubPath: subPath}, nil
}
}
- return nil, "", errors.New("could not match lookup path")
+ // TODO improve code around default serving, when `disk` serving gets removed
+ // https://gitlab.com/gitlab-org/gitlab-pages/issues/353
+ return &serving.Request{Serving: defaultServing()},
+ errors.New("could not match lookup path")
}
diff --git a/internal/source/gitlab/gitlab_test.go b/internal/source/gitlab/gitlab_test.go
index 0e855f10..e6f194ee 100644
--- a/internal/source/gitlab/gitlab_test.go
+++ b/internal/source/gitlab/gitlab_test.go
@@ -39,62 +39,62 @@ func TestResolve(t *testing.T) {
target := "https://test.gitlab.io:443/my/pages/project/"
request := httptest.NewRequest("GET", target, nil)
- lookup, subpath, err := source.Resolve(request)
+ response, err := source.Resolve(request)
require.NoError(t, err)
- require.Equal(t, "/my/pages/project/", lookup.Prefix)
- require.Equal(t, "some/path/to/project/", lookup.Path)
- require.Equal(t, "", subpath)
- require.False(t, lookup.IsNamespaceProject)
+ require.Equal(t, "/my/pages/project/", response.LookupPath.Prefix)
+ require.Equal(t, "some/path/to/project/", response.LookupPath.Path)
+ require.Equal(t, "", response.SubPath)
+ require.False(t, response.LookupPath.IsNamespaceProject)
})
t.Run("when requesting a nested group project with full path", func(t *testing.T) {
target := "https://test.gitlab.io:443/my/pages/project/path/index.html"
request := httptest.NewRequest("GET", target, nil)
- lookup, subpath, err := source.Resolve(request)
+ response, err := source.Resolve(request)
require.NoError(t, err)
- require.Equal(t, "/my/pages/project/", lookup.Prefix)
- require.Equal(t, "some/path/to/project/", lookup.Path)
- require.Equal(t, "path/index.html", subpath)
- require.False(t, lookup.IsNamespaceProject)
+ require.Equal(t, "/my/pages/project/", response.LookupPath.Prefix)
+ require.Equal(t, "some/path/to/project/", response.LookupPath.Path)
+ require.Equal(t, "path/index.html", response.SubPath)
+ require.False(t, response.LookupPath.IsNamespaceProject)
})
t.Run("when requesting the group root project with root path", func(t *testing.T) {
target := "https://test.gitlab.io:443/"
request := httptest.NewRequest("GET", target, nil)
- lookup, subpath, err := source.Resolve(request)
+ response, err := source.Resolve(request)
require.NoError(t, err)
- require.Equal(t, "/", lookup.Prefix)
- require.Equal(t, "some/path/to/project-3/", lookup.Path)
- require.Equal(t, "", subpath)
- require.True(t, lookup.IsNamespaceProject)
+ require.Equal(t, "/", response.LookupPath.Prefix)
+ require.Equal(t, "some/path/to/project-3/", response.LookupPath.Path)
+ require.Equal(t, "", response.SubPath)
+ require.True(t, response.LookupPath.IsNamespaceProject)
})
t.Run("when requesting the group root project with full path", func(t *testing.T) {
target := "https://test.gitlab.io:443/path/to/index.html"
request := httptest.NewRequest("GET", target, nil)
- lookup, subpath, err := source.Resolve(request)
+ response, err := source.Resolve(request)
require.NoError(t, err)
- require.Equal(t, "/", lookup.Prefix)
- require.Equal(t, "path/to/index.html", subpath)
- require.Equal(t, "some/path/to/project-3/", lookup.Path)
- require.True(t, lookup.IsNamespaceProject)
+ require.Equal(t, "/", response.LookupPath.Prefix)
+ require.Equal(t, "path/to/index.html", response.SubPath)
+ require.Equal(t, "some/path/to/project-3/", response.LookupPath.Path)
+ require.True(t, response.LookupPath.IsNamespaceProject)
})
t.Run("when request path has not been sanitized", func(t *testing.T) {
target := "https://test.gitlab.io:443/something/../something/../my/pages/project/index.html"
request := httptest.NewRequest("GET", target, nil)
- lookup, subpath, err := source.Resolve(request)
+ response, err := source.Resolve(request)
require.NoError(t, err)
- require.Equal(t, "/my/pages/project/", lookup.Prefix)
- require.Equal(t, "index.html", subpath)
+ require.Equal(t, "/my/pages/project/", response.LookupPath.Prefix)
+ require.Equal(t, "index.html", response.SubPath)
})
}