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

gitlab.go « gitlab « source « internal - gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: abe645fe73fbf1de2adc2ed8e854938860d28abe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package gitlab

import (
	"context"
	"errors"
	"net/http"
	"path"
	"sort"
	"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, 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)
	size := len(response.Domain.LookupPaths)

	sortLookupsByPrefixLengthDesc(response.Domain.LookupPaths)

	for _, lookup := range response.Domain.LookupPaths {
		isSubPath := strings.HasPrefix(urlPath, lookup.Prefix)
		isRootPath := urlPath == path.Clean(lookup.Prefix)

		if isSubPath || isRootPath {
			subPath := ""
			if isSubPath {
				subPath = strings.TrimPrefix(urlPath, lookup.Prefix)
			}

			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
}

// Ensure lookupPaths are sorted by prefix length to ensure the group level
// domain with prefix "/" is the last one to be checked.
// See https://gitlab.com/gitlab-org/gitlab-pages/-/issues/576
func sortLookupsByPrefixLengthDesc(lookups []api.LookupPath) {
	sort.SliceStable(lookups, func(i, j int) bool {
		return len(lookups[i].Prefix) > len(lookups[j].Prefix)
	})
}