Skip to content

Commit a536f61

Browse files
authored
Implement repo register/deregister for gitlab (#4489)
* Implement repo register/deregister for gitlab This implements the webhook creation and deletion for gitlab. Signed-off-by: Juan Antonio Osorio <[email protected]> * Implement webhook cleanup and use correct structs Signed-off-by: Juan Antonio Osorio <[email protected]> * Implement unique webhook URL for each entity in gitlab Signed-off-by: Juan Antonio Osorio <[email protected]> * Ensure / at the end of provider webhook handlers. Signed-off-by: Juan Antonio Osorio <[email protected]> --------- Signed-off-by: Juan Antonio Osorio <[email protected]>
1 parent fe9cd23 commit a536f61

File tree

8 files changed

+292
-41
lines changed

8 files changed

+292
-41
lines changed

cmd/dev/app/rule_type/rttst.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,8 @@ func getProvider(pstr string, token string, providerConfigFile string) (provifv1
437437
return nil, fmt.Errorf("error parsing gitlab provider config: %w", err)
438438
}
439439

440-
client, err := gitlab.New(credentials.NewGitLabTokenCredential(token), cfg)
440+
// We may pass a "fake" webhook URL here as it is not used in the test
441+
client, err := gitlab.New(credentials.NewGitLabTokenCredential(token), cfg, "fake")
441442
if err != nil {
442443
return nil, fmt.Errorf("error instantiating gitlab provider: %w", err)
443444
}

internal/controlplane/server.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"net/http"
2323
"net/url"
2424
"runtime/debug"
25+
"strings"
2526
"time"
2627

2728
"github.com/gorilla/handlers"
@@ -357,7 +358,12 @@ func (s *Server) StartHTTPServer(ctx context.Context) error {
357358
// Register the webhook handlers
358359
// Note: The GitHub webhook handler is not registered here.
359360
for classpath, handler := range s.providerManager.IterateWebhookHandlers() {
360-
path, err := url.JoinPath("/api/v1/webhook/", url.PathEscape(classpath))
361+
classpath = url.PathEscape(classpath)
362+
if !strings.HasSuffix(classpath, "/") {
363+
classpath += "/"
364+
}
365+
366+
path, err := url.JoinPath("/api/v1/webhook/", classpath)
361367
if err != nil {
362368
return fmt.Errorf("failed to register webhook handler for %s: %w", classpath, err)
363369
}

internal/providers/gitlab/gitlab.go

+15-28
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626

2727
config "github.com/stacklok/minder/internal/config/server"
2828
"github.com/stacklok/minder/internal/db"
29-
"github.com/stacklok/minder/internal/entities/properties"
3029
minderv1 "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1"
3130
provifv1 "github.com/stacklok/minder/pkg/providers/v1"
3231
)
@@ -53,25 +52,32 @@ var _ provifv1.REST = (*gitlabClient)(nil)
5352
var _ provifv1.RepoLister = (*gitlabClient)(nil)
5453

5554
type gitlabClient struct {
56-
cred provifv1.GitLabCredential
57-
cli *http.Client
58-
glcfg *minderv1.GitLabProviderConfig
59-
gitConfig config.GitConfig
55+
cred provifv1.GitLabCredential
56+
cli *http.Client
57+
glcfg *minderv1.GitLabProviderConfig
58+
webhookURL string
59+
gitConfig config.GitConfig
6060
}
6161

6262
// New creates a new GitLab provider
63-
func New(cred provifv1.GitLabCredential, cfg *minderv1.GitLabProviderConfig) (*gitlabClient, error) {
63+
// Note that the webhook URL should already contain the provider class in the path
64+
func New(cred provifv1.GitLabCredential, cfg *minderv1.GitLabProviderConfig, webhookURL string) (*gitlabClient, error) {
6465
// TODO: We need a context here.
6566
cli := oauth2.NewClient(context.Background(), cred.GetAsOAuth2TokenSource())
6667

6768
if cfg.Endpoint == "" {
6869
cfg.Endpoint = "https://gitlab.com/api/v4/"
6970
}
7071

72+
if webhookURL == "" {
73+
return nil, errors.New("webhook URL is required")
74+
}
75+
7176
return &gitlabClient{
72-
cred: cred,
73-
cli: cli,
74-
glcfg: cfg,
77+
cred: cred,
78+
cli: cli,
79+
glcfg: cfg,
80+
webhookURL: webhookURL,
7581
// TODO: Add git config
7682
}, nil
7783
}
@@ -128,22 +134,3 @@ func (c *gitlabClient) GetCredential() provifv1.GitLabCredential {
128134
func (_ *gitlabClient) SupportsEntity(entType minderv1.Entity) bool {
129135
return entType == minderv1.Entity_ENTITY_REPOSITORIES
130136
}
131-
132-
// RegisterEntity implements the Provider interface
133-
func (c *gitlabClient) RegisterEntity(
134-
_ context.Context, entType minderv1.Entity, props *properties.Properties,
135-
) (*properties.Properties, error) {
136-
if !c.SupportsEntity(entType) {
137-
return nil, errors.New("unsupported entity type")
138-
}
139-
140-
// TODO: implement
141-
142-
return props, nil
143-
}
144-
145-
// DeregisterEntity implements the Provider interface
146-
func (_ *gitlabClient) DeregisterEntity(_ context.Context, _ minderv1.Entity, _ *properties.Properties) error {
147-
// TODO: implement
148-
return nil
149-
}

internal/providers/gitlab/manager/manager.go

+24-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/json"
2222
"errors"
2323
"fmt"
24+
"net/url"
2425
"slices"
2526

2627
"github.com/stacklok/minder/internal/config/server"
@@ -35,16 +36,32 @@ type providerClassManager struct {
3536
store db.Store
3637
crypteng crypto.Engine
3738
// gitlab provider config
38-
glpcfg *server.GitLabConfig
39+
glpcfg *server.GitLabConfig
40+
webhookURL string
41+
parentContext context.Context
3942
}
4043

4144
// NewGitLabProviderClassManager creates a new provider class manager for the dockerhub provider
42-
func NewGitLabProviderClassManager(crypteng crypto.Engine, store db.Store, cfg *server.GitLabConfig) *providerClassManager {
43-
return &providerClassManager{
44-
store: store,
45-
crypteng: crypteng,
46-
glpcfg: cfg,
45+
func NewGitLabProviderClassManager(
46+
ctx context.Context, crypteng crypto.Engine, store db.Store, cfg *server.GitLabConfig, wgCfg server.WebhookConfig,
47+
) (*providerClassManager, error) {
48+
webhookURLBase := wgCfg.ExternalWebhookURL
49+
if webhookURLBase == "" {
50+
return nil, errors.New("webhook URL is required")
51+
}
52+
53+
webhookURL, err := url.JoinPath(webhookURLBase, url.PathEscape(string(db.ProviderClassGitlab)))
54+
if err != nil {
55+
return nil, fmt.Errorf("error joining webhook URL: %w", err)
4756
}
57+
58+
return &providerClassManager{
59+
store: store,
60+
crypteng: crypteng,
61+
glpcfg: cfg,
62+
webhookURL: webhookURL,
63+
parentContext: ctx,
64+
}, nil
4865
}
4966

5067
// GetSupportedClasses implements the ProviderClassManager interface
@@ -74,7 +91,7 @@ func (g *providerClassManager) Build(ctx context.Context, config *db.Provider) (
7491
return nil, fmt.Errorf("error parsing gitlab config: %w", err)
7592
}
7693

77-
cli, err := gitlab.New(creds, cfg)
94+
cli, err := gitlab.New(creds, cfg, g.webhookURL)
7895
if err != nil {
7996
return nil, fmt.Errorf("error creating gitlab client: %w", err)
8097
}

internal/providers/gitlab/manager/webhook.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,28 @@
1414

1515
package manager
1616

17-
import "net/http"
17+
import (
18+
"net/http"
19+
20+
"github.com/rs/zerolog"
21+
)
1822

1923
// GetWebhookHandler implements the ProviderManager interface
2024
// Note that this is where the whole webhook handler is defined and
2125
// will live.
22-
func (_ *providerClassManager) GetWebhookHandler() http.Handler {
23-
return nil
26+
func (m *providerClassManager) GetWebhookHandler() http.Handler {
27+
return http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
28+
l := zerolog.Ctx(m.parentContext).With().
29+
Str("webhook", "gitlab").
30+
Str("method", r.Method).
31+
Str("path", r.URL.Path).
32+
Str("remote", r.RemoteAddr).
33+
Str("user-agent", r.UserAgent()).
34+
Str("content-type", r.Header.Get("Content-Type")).
35+
Logger()
36+
37+
// TODO: Implement webhook handler
38+
39+
l.Debug().Msg("received webhook")
40+
})
2441
}

internal/providers/gitlab/properties.go

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const (
3737
RepoPropertyLicense = "gitlab/license"
3838
// RepoPropertyCloneURL represents the gitlab repo clone URL
3939
RepoPropertyCloneURL = "gitlab/clone_url"
40+
// RepoPropertyHookID represents the gitlab repo hook ID
41+
RepoPropertyHookID = "gitlab/hook_id"
42+
// RepoPropertyHookURL represents the gitlab repo hook URL
43+
RepoPropertyHookURL = "gitlab/hook_url"
4044
)
4145

4246
// FetchAllProperties implements the provider interface

0 commit comments

Comments
 (0)