internal/source/gitlab/gitlab.go (81 lines of code) (raw):
package gitlab
import (
"context"
"errors"
"net/http"
"path"
"strings"
"gitlab.com/gitlab-org/labkit/log"
"gitlab.com/gitlab-org/gitlab-pages/internal/config"
"gitlab.com/gitlab-org/gitlab-pages/internal/domain"
"gitlab.com/gitlab-org/gitlab-pages/internal/logging"
"gitlab.com/gitlab-org/gitlab-pages/internal/request"
"gitlab.com/gitlab-org/gitlab-pages/internal/serving"
"gitlab.com/gitlab-org/gitlab-pages/internal/source/gitlab/api"
"gitlab.com/gitlab-org/gitlab-pages/internal/source/gitlab/cache"
"gitlab.com/gitlab-org/gitlab-pages/internal/source/gitlab/client"
)
// Gitlab source represent a new domains configuration source. We fetch all the
// information about domains from GitLab instance.
type Gitlab struct {
client api.Resolver
enableDisk bool
}
// New returns a new instance of gitlab domain source.
func New(cfg *config.GitLab) (*Gitlab, error) {
glClient, err := client.NewFromConfig(cfg)
if err != nil {
return nil, err
}
g := &Gitlab{
client: cache.NewCache(glClient, &cfg.Cache),
enableDisk: cfg.EnableDisk,
}
return g, nil
}
// GetDomain return a representation of a domain that we have fetched from
// GitLab
func (g *Gitlab) GetDomain(ctx context.Context, name string) (*domain.Domain, error) {
lookup := g.client.Resolve(ctx, name)
if lookup.Error != nil {
if errors.Is(lookup.Error, client.ErrUnauthorizedAPI) {
log.WithError(lookup.Error).Error("Pages cannot communicate with an instance of the GitLab API. Please sync your gitlab-secrets.json file: https://docs.gitlab.com/ee/administration/pages/#pages-cannot-communicate-with-an-instance-of-the-gitlab-api")
}
return nil, lookup.Error
}
// TODO introduce a second-level cache for domains, invalidate using etags
// from first-level cache
d := domain.New(name, lookup.Domain.Certificate, lookup.Domain.Key, lookup.Domain.ClientCertificate, g)
return d, nil
}
// 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
}
urlPath := path.Clean(r.URL.Path)
if urlPath == "." {
// If the result of Clean process is an empty string, it returns the string "."
urlPath = "/"
}
lowerURLPath := strings.ToLower(urlPath)
size := len(response.Domain.LookupPaths)
for _, lookup := range response.Domain.LookupPaths {
lookupPrefix := strings.ToLower(lookup.Prefix)
isSubPath := strings.HasPrefix(lowerURLPath, lookupPrefix)
isRootPath := lowerURLPath == path.Clean(lookupPrefix)
if isSubPath || isRootPath {
subPath := ""
if isSubPath {
subPath = urlPath[len(lookupPrefix):]
}
srv, err := g.fabricateServing(lookup)
if err != nil {
return nil, err
}
return &serving.Request{
Serving: srv,
LookupPath: fabricateLookupPath(size, lookup),
SubPath: subPath}, nil
}
}
logging.LogRequest(r).WithError(domain.ErrDomainDoesNotExist).WithFields(
log.Fields{
"lookup_paths_count": size,
"lookup_paths": response.Domain.LookupPaths,
}).Error("could not find project lookup path")
return nil, domain.ErrDomainDoesNotExist
}