From cbf3adc89622014a78c28dde584ffaea76821c5f Mon Sep 17 00:00:00 2001 From: omri assa Date: Wed, 12 Jun 2024 21:15:01 +0300 Subject: [PATCH 01/20] fix: gitlab get scopes added --- go.mod | 3 + go.sum | 7 + pkg/git_provider/gitlab.go | 383 +++++++++++++++++++++++++++++++ pkg/git_provider/gitlab_utils.go | 73 ++++++ 4 files changed, 466 insertions(+) create mode 100644 pkg/git_provider/gitlab.go create mode 100644 pkg/git_provider/gitlab_utils.go diff --git a/go.mod b/go.mod index 264a837..245bbc6 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,8 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -67,6 +69,7 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + github.com/xanzy/go-gitlab v0.105.0 // indirect github.com/yhirose/go-peg v0.0.0-20210804202551-de25d6753cf1 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.17.0 // indirect diff --git a/go.sum b/go.sum index c788bdd..b22342d 100644 --- a/go.sum +++ b/go.sum @@ -879,6 +879,11 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= +github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -1032,6 +1037,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xanzy/go-gitlab v0.105.0 h1:3nyLq0ESez0crcaM19o5S//SvezOQguuIHZ3wgX64hM= +github.com/xanzy/go-gitlab v0.105.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI= github.com/yhirose/go-peg v0.0.0-20210804202551-de25d6753cf1 h1:7iTmQ0lZwTtfm4XMgP5ezzWMDCjo7GTS0ZgCj6jpVzM= github.com/yhirose/go-peg v0.0.0-20210804202551-de25d6753cf1/go.mod h1:q2QWLflHsZxT6ixYcXveTYicEvxGh5Uv6CnI7f7BfjQ= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go new file mode 100644 index 0000000..fe26671 --- /dev/null +++ b/pkg/git_provider/gitlab.go @@ -0,0 +1,383 @@ +package git_provider + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/quickube/piper/pkg/conf" + + "github.com/xanzy/go-gitlab" +) + +type GitlabClientImpl struct { + client *gitlab.Client + cfg *conf.GlobalConfig +} + +func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { + ctx := context.Background() + + client, err := gitlab.NewClient(cfg.GitProviderConfig.Token) + if err != nil { + log.Fatalf("Failed to create client: %v", err) + } + + + err := ValidateGitlabPermissions(ctx, client, cfg) + if err != nil { + return nil, fmt.Errorf("failed to validate permissions: %v", err) + } + user, resp, err := client.Users.Get(context.Background(), cfg.OrgName) + if err != nil { + return nil, fmt.Errorf("failed to get org id: %v", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to get organization id %s", resp.Status) + } + + cfg.OrgID = user.GetID() + + log.Printf("Org ID is: %d\n", cfg.OrgID) + + return &GitlabClientImpl{ + client: client, + cfg: cfg, + }, err +} + +// func (c *GithubClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { +// var files []string + +// opt := &github.RepositoryContentGetOptions{Ref: branch} +// _, directoryContent, resp, err := c.client.Repositories.GetContents(*ctx, c.cfg.GitProviderConfig.OrgName, repo, path, opt) +// if err != nil { +// return nil, err +// } +// if resp.StatusCode != 200 { +// return nil, fmt.Errorf("github provider returned %d: failed to get contents of %s/%s%s", resp.StatusCode, repo, branch, path) +// } +// if directoryContent == nil { +// return nil, nil +// } +// for _, file := range directoryContent { +// files = append(files, file.GetName()) +// } +// return files, nil +// } + +// func (c *GithubClientImpl) GetFile(ctx *context.Context, repo string, branch string, path string) (*CommitFile, error) { +// var commitFile CommitFile + +// opt := &github.RepositoryContentGetOptions{Ref: branch} +// fileContent, _, resp, err := c.client.Repositories.GetContents(*ctx, c.cfg.GitProviderConfig.OrgName, repo, path, opt) +// if err != nil { +// return &commitFile, err +// } +// if resp.StatusCode == 404 { +// log.Printf("File %s not found in repo %s branch %s", path, repo, branch) +// return nil, nil +// } +// if resp.StatusCode != 200 { +// return &commitFile, err +// } +// if fileContent == nil { +// return &commitFile, nil +// } +// filePath := fileContent.GetPath() +// commitFile.Path = &filePath +// fileContentString, err := fileContent.GetContent() +// if err != nil { +// return &commitFile, err +// } +// commitFile.Content = &fileContentString + +// return &commitFile, nil +// } + +// func (c *GithubClientImpl) GetFiles(ctx *context.Context, repo string, branch string, paths []string) ([]*CommitFile, error) { +// var commitFiles []*CommitFile +// for _, path := range paths { +// file, err := c.GetFile(ctx, repo, branch, path) +// if err != nil { +// return nil, err +// } +// if file == nil { +// log.Printf("file %s not found in repo %s branch %s", path, repo, branch) +// continue +// } +// commitFiles = append(commitFiles, file) +// } +// return commitFiles, nil +// } + +// func (c *GithubClientImpl) SetWebhook(ctx *context.Context, repo *string) (*HookWithStatus, error) { +// if c.cfg.OrgLevelWebhook && repo != nil { +// return nil, fmt.Errorf("trying to set repo scope. repo: %s", *repo) +// } + +// hookConf := &github.Hook{ +// Config: map[string]interface{}{ +// "url": c.cfg.GitProviderConfig.WebhookURL, +// "content_type": "json", +// "secret": c.cfg.GitProviderConfig.WebhookSecret, +// }, +// Events: []string{"push", "pull_request", "create", "release"}, +// Active: github.Bool(true), +// } + +// if repo == nil { +// respHook, ok := isOrgWebhookEnabled(*ctx, c) +// if !ok { +// createdHook, resp, err := c.client.Organizations.CreateHook( +// *ctx, +// c.cfg.GitProviderConfig.OrgName, +// hookConf, +// ) +// if err != nil { +// return nil, err +// } +// if resp.StatusCode != 201 { +// return nil, fmt.Errorf("failed to create org level webhhok, API returned %d", resp.StatusCode) +// } +// log.Printf("edited webhook of type %s for %s name: %s\n", createdHook.GetType(), c.cfg.GitProviderConfig.OrgName, createdHook.Config["url"]) +// hookID := createdHook.GetID() +// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil +// } else { +// updatedHook, resp, err := c.client.Organizations.EditHook( +// *ctx, +// c.cfg.GitProviderConfig.OrgName, +// respHook.GetID(), +// hookConf, +// ) +// if err != nil { +// return nil, err +// } +// if resp.StatusCode != http.StatusOK { +// return nil, fmt.Errorf( +// "failed to update org level webhhok for %s, API returned %d", +// c.cfg.GitProviderConfig.OrgName, +// resp.StatusCode, +// ) +// } +// log.Printf("edited webhook of type %s for %s: %s\n", updatedHook.GetType(), c.cfg.GitProviderConfig.OrgName, updatedHook.Config["url"]) +// hookID := updatedHook.GetID() +// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil +// } +// } else { +// respHook, ok := isRepoWebhookEnabled(*ctx, c, *repo) +// if !ok { +// createdHook, resp, err := c.client.Repositories.CreateHook(*ctx, c.cfg.GitProviderConfig.OrgName, *repo, hookConf) +// if err != nil { +// return nil, err +// } + +// if resp.StatusCode != 201 { +// return nil, fmt.Errorf("failed to create repo level webhhok for %s, API returned %d", *repo, resp.StatusCode) +// } +// log.Printf("created webhook of type %s for %s: %s\n", createdHook.GetType(), *repo, createdHook.Config["url"]) +// hookID := createdHook.GetID() +// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil +// } else { +// updatedHook, resp, err := c.client.Repositories.EditHook(*ctx, c.cfg.GitProviderConfig.OrgName, *repo, respHook.GetID(), hookConf) +// if err != nil { +// return nil, err +// } +// if resp.StatusCode != http.StatusOK { +// return nil, fmt.Errorf("failed to update repo level webhhok for %s, API returned %d", *repo, resp.StatusCode) +// } +// log.Printf("edited webhook of type %s for %s: %s\n", updatedHook.GetType(), *repo, updatedHook.Config["url"]) +// hookID := updatedHook.GetID() +// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil +// } + +// } +// } + +// func (c *GithubClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStatus) error { + +// if hook.RepoName == nil { + +// resp, err := c.client.Organizations.DeleteHook(*ctx, c.cfg.GitProviderConfig.OrgName, hook.HookID) + +// if err != nil { +// return err +// } + +// if resp.StatusCode != 204 { +// return fmt.Errorf("failed to delete org level webhhok, API call returned %d", resp.StatusCode) +// } +// log.Printf("removed org webhook, hookID :%d\n", hook.HookID) // INFO +// } else { +// resp, err := c.client.Repositories.DeleteHook(*ctx, c.cfg.GitProviderConfig.OrgName, *hook.RepoName, hook.HookID) + +// if err != nil { +// return fmt.Errorf("failed to delete repo level webhhok for %s, API call returned %d. %s", *hook.RepoName, resp.StatusCode, err) +// } + +// if resp.StatusCode != 204 { +// return fmt.Errorf("failed to delete repo level webhhok for %s, API call returned %d", *hook.RepoName, resp.StatusCode) +// } +// log.Printf("removed repo webhook, repo:%s hookID :%d\n", *hook.RepoName, hook.HookID) // INFO +// } + +// return nil +// } + +// func (c *GithubClientImpl) HandlePayload(ctx *context.Context, request *http.Request, secret []byte) (*WebhookPayload, error) { +// var webhookPayload *WebhookPayload + +// payload, err := github.ValidatePayload(request, secret) +// if err != nil { +// return nil, err +// } + +// event, err := github.ParseWebHook(github.WebHookType(request), payload) +// if err != nil { +// return nil, err +// } + +// switch e := event.(type) { +// case *github.PingEvent: +// webhookPayload = &WebhookPayload{ +// Event: "ping", +// Repo: e.GetRepo().GetFullName(), +// HookID: e.GetHookID(), +// OwnerID: e.GetSender().GetID(), +// } +// case *github.PushEvent: +// webhookPayload = &WebhookPayload{ +// Event: "push", +// Action: e.GetAction(), +// Repo: e.GetRepo().GetName(), +// Branch: strings.TrimPrefix(e.GetRef(), "refs/heads/"), +// Commit: e.GetHeadCommit().GetID(), +// User: e.GetSender().GetLogin(), +// UserEmail: e.GetHeadCommit().GetAuthor().GetEmail(), +// OwnerID: e.GetSender().GetID(), +// } +// case *github.PullRequestEvent: +// webhookPayload = &WebhookPayload{ +// Event: "pull_request", +// Action: e.GetAction(), +// Repo: e.GetRepo().GetName(), +// Branch: e.GetPullRequest().GetHead().GetRef(), +// Commit: e.GetPullRequest().GetHead().GetSHA(), +// User: e.GetPullRequest().GetUser().GetLogin(), +// UserEmail: e.GetSender().GetEmail(), // e.GetPullRequest().GetUser().GetEmail() Not working. GitHub missing email for PR events in payload. +// PullRequestTitle: e.GetPullRequest().GetTitle(), +// PullRequestURL: e.GetPullRequest().GetHTMLURL(), +// DestBranch: e.GetPullRequest().GetBase().GetRef(), +// Labels: c.extractLabelNames(e.GetPullRequest().Labels), +// OwnerID: e.GetSender().GetID(), +// } +// case *github.CreateEvent: +// webhookPayload = &WebhookPayload{ +// Event: "create", +// Action: e.GetRefType(), // Possible values are: "repository", "branch", "tag". +// Repo: e.GetRepo().GetName(), +// Branch: e.GetRef(), +// Commit: e.GetRef(), +// User: e.GetSender().GetLogin(), +// UserEmail: e.GetSender().GetEmail(), +// OwnerID: e.GetSender().GetID(), +// } +// case *github.ReleaseEvent: +// commitSHA, _err := c.refToSHA(ctx, e.GetRelease().GetName(), e.GetRepo().GetName()) +// if _err != nil { +// return webhookPayload, _err +// } +// webhookPayload = &WebhookPayload{ +// Event: "release", +// Action: e.GetAction(), // "created", "edited", "deleted", or "prereleased". +// Repo: e.GetRepo().GetName(), +// Branch: e.GetRelease().GetTagName(), +// Commit: *commitSHA, +// User: e.GetSender().GetLogin(), +// UserEmail: e.GetSender().GetEmail(), +// OwnerID: e.GetSender().GetID(), +// } +// } + +// if c.cfg.EnforceOrgBelonging && (webhookPayload.OwnerID == 0 || webhookPayload.OwnerID != c.cfg.OrgID) { +// return nil, fmt.Errorf("webhook send from non organizational member") +// } +// return webhookPayload, nil + +// } + +// func (c *GithubClientImpl) SetStatus(ctx *context.Context, repo *string, commit *string, linkURL *string, status *string, message *string) error { +// if !utils.ValidateHTTPFormat(*linkURL) { +// return fmt.Errorf("invalid linkURL") +// } +// repoStatus := &github.RepoStatus{ +// State: status, // pending, success, error, or failure. +// TargetURL: linkURL, +// Description: utils.SPtr(fmt.Sprintf("Workflow %s %s", *status, *message)), +// Context: utils.SPtr("Piper/ArgoWorkflows"), +// AvatarURL: utils.SPtr("https://argoproj.github.io/argo-workflows/assets/logo.png"), +// } + +// _, resp, err := c.client.Repositories.CreateStatus(*ctx, c.cfg.OrgName, *repo, *commit, repoStatus) +// if err != nil { +// return err +// } + +// if resp.StatusCode != http.StatusCreated { +// return fmt.Errorf("failed to set status on repo:%s, commit:%s, API call returned %d", *repo, *commit, resp.StatusCode) +// } + +// log.Printf("successfully set status on repo:%s commit: %s to status: %s\n", *repo, *commit, *status) +// return nil +// } + +// func (c *GithubClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) error { +// if c.cfg.OrgLevelWebhook && hook.RepoName != nil { +// return fmt.Errorf("trying to ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) +// } +// if hook.RepoName == nil { +// resp, err := c.client.Organizations.PingHook(*ctx, c.cfg.OrgName, hook.HookID) +// if err != nil { +// return err +// } + +// if resp.StatusCode == http.StatusNotFound { +// return fmt.Errorf("unable to find organization webhook for hookID: %d", hook.HookID) +// } +// } else { +// resp, err := c.client.Repositories.PingHook(*ctx, c.cfg.GitProviderConfig.OrgName, *hook.RepoName, hook.HookID) +// if err != nil { +// return err +// } + +// if resp.StatusCode == http.StatusNotFound { +// return fmt.Errorf("unable to find repo webhook for repo:%s hookID: %d", *hook.RepoName, hook.HookID) +// } +// } + +// return nil +// } + +// func (c *GithubClientImpl) refToSHA(ctx *context.Context, ref string, repo string) (*string, error) { +// respSHA, resp, err := c.client.Repositories.GetCommitSHA1(*ctx, c.cfg.OrgName, repo, ref, "") +// if err != nil { +// return nil, err +// } + +// if resp.StatusCode != http.StatusOK { +// return nil, fmt.Errorf("failed to set status on repo:%s, commit:%s, API call returned %d", repo, ref, resp.StatusCode) +// } + +// log.Printf("resolved ref: %s to SHA: %s", ref, respSHA) +// return &respSHA, nil +// } + +// func (c *GithubClientImpl) extractLabelNames(labels []*github.Label) []string { +// var returnLabelsList []string +// for _, label := range labels { +// returnLabelsList = append(returnLabelsList, *label.Name) +// } +// return returnLabelsList +// } diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go new file mode 100644 index 0000000..b80f5c4 --- /dev/null +++ b/pkg/git_provider/gitlab_utils.go @@ -0,0 +1,73 @@ +package git_provider + +import ( + "fmt" + "log" + "net/http" + "strings" + + "github.com/hashicorp/go-retryablehttp" + "github.com/quickube/piper/pkg/conf" + "github.com/quickube/piper/pkg/utils" + "github.com/xanzy/go-gitlab" + "golang.org/x/net/context" +) + +func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg *conf.GlobalConfig) error { + + orgScopes := []string{"admin:org_hook"} + repoAdminScopes := []string{"admin:repo_hook"} + repoGranularScopes := []string{"write:repo_hook", "read:repo_hook"} + + scopes, err := GetGitlabScopes(ctx, client) + + if err != nil { + return fmt.Errorf("failed to get scopes: %v", err) + } + if len(scopes) == 0 { + return fmt.Errorf("permissions error: no scopes found for the gitlab client") + } + + if cfg.GitProviderConfig.OrgLevelWebhook { + if utils.ListContains(orgScopes, scopes) { + return nil + } + return fmt.Errorf("permissions error: %v is not a valid scope for the org level permissions", scopes) + } + + if utils.ListContains(repoAdminScopes, scopes) { + return nil + } + if utils.ListContains(repoGranularScopes, scopes) { + return nil + } + + return fmt.Errorf("permissions error: %v is not a valid scope for the repo level permissions", scopes) +} + +func GetGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, error) { + req, err := retryablehttp.NewRequest("GET", "https://gitlab.com/api/v4/user", nil) + if err != nil { + log.Fatalf("Failed to create request: %v", err) + } + + resp, err := client.Do(req, nil) + if err != nil { + log.Fatalf("Failed to perform request: %v", err) + return nil, err + + } + defer resp.Body.Close() + + // Check for successful response + if resp.StatusCode != http.StatusOK { + log.Fatalf("Failed to get user: %v", resp.Status) + return nil, err + } + + scopes := resp.Header.Get("X-OAuth-Scopes") + fmt.Println("Github Token Scopes are:", scopes) + + scopes = strings.ReplaceAll(scopes, " ", "") + return strings.Split(scopes, ","), nil +} \ No newline at end of file From 70bc0a0161e2eee3e0a6451502bcf69270c5a89c Mon Sep 17 00:00:00 2001 From: omri assa Date: Wed, 12 Jun 2024 21:43:50 +0300 Subject: [PATCH 02/20] fix: finished newgitlabClient --- pkg/git_provider/gitlab.go | 72 ++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index fe26671..95ef769 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -6,6 +6,7 @@ import ( "log" "net/http" + "github.com/google/go-github/v52/github" "github.com/quickube/piper/pkg/conf" "github.com/xanzy/go-gitlab" @@ -24,21 +25,21 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { log.Fatalf("Failed to create client: %v", err) } - - err := ValidateGitlabPermissions(ctx, client, cfg) + err = ValidateGitlabPermissions(ctx, client, cfg) if err != nil { return nil, fmt.Errorf("failed to validate permissions: %v", err) } - user, resp, err := client.Users.Get(context.Background(), cfg.OrgName) + + group, resp, err := client.Groups.GetGroup(cfg.OrgName, nil) if err != nil { - return nil, fmt.Errorf("failed to get org id: %v", err) + return nil, fmt.Errorf("failed to get organization: %v", err) } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to get organization id %s", resp.Status) + return nil, fmt.Errorf("failed to get organization data %s", resp.Status) } - cfg.OrgID = user.GetID() + cfg.OrgID = int64(group.ID) log.Printf("Org ID is: %d\n", cfg.OrgID) @@ -48,7 +49,8 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { }, err } -// func (c *GithubClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { + +func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { // var files []string // opt := &github.RepositoryContentGetOptions{Ref: branch} @@ -66,9 +68,10 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // files = append(files, file.GetName()) // } // return files, nil -// } + panic("implement me") +} -// func (c *GithubClientImpl) GetFile(ctx *context.Context, repo string, branch string, path string) (*CommitFile, error) { +func (c *GitlabClientImpl) GetFile(ctx *context.Context, repo string, branch string, path string) (*CommitFile, error) { // var commitFile CommitFile // opt := &github.RepositoryContentGetOptions{Ref: branch} @@ -95,9 +98,10 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // commitFile.Content = &fileContentString // return &commitFile, nil -// } + panic("implement me") +} -// func (c *GithubClientImpl) GetFiles(ctx *context.Context, repo string, branch string, paths []string) ([]*CommitFile, error) { +func (c *GitlabClientImpl) GetFiles(ctx *context.Context, repo string, branch string, paths []string) ([]*CommitFile, error) { // var commitFiles []*CommitFile // for _, path := range paths { // file, err := c.GetFile(ctx, repo, branch, path) @@ -111,9 +115,10 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // commitFiles = append(commitFiles, file) // } // return commitFiles, nil -// } +panic("implement me") +} -// func (c *GithubClientImpl) SetWebhook(ctx *context.Context, repo *string) (*HookWithStatus, error) { +func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*HookWithStatus, error) { // if c.cfg.OrgLevelWebhook && repo != nil { // return nil, fmt.Errorf("trying to set repo scope. repo: %s", *repo) // } @@ -126,7 +131,7 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // }, // Events: []string{"push", "pull_request", "create", "release"}, // Active: github.Bool(true), -// } + // } // if repo == nil { // respHook, ok := isOrgWebhookEnabled(*ctx, c) @@ -194,9 +199,11 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // } // } -// } +panic("implement me") + +} -// func (c *GithubClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStatus) error { +func (c *GitlabClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStatus) error { // if hook.RepoName == nil { @@ -224,9 +231,11 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // } // return nil -// } +panic("implement me") + +} -// func (c *GithubClientImpl) HandlePayload(ctx *context.Context, request *http.Request, secret []byte) (*WebhookPayload, error) { +func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Request, secret []byte) (*WebhookPayload, error) { // var webhookPayload *WebhookPayload // payload, err := github.ValidatePayload(request, secret) @@ -305,10 +314,13 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // return nil, fmt.Errorf("webhook send from non organizational member") // } // return webhookPayload, nil +panic("implement me") -// } +} + +func (c *GitlabClientImpl) SetStatus(ctx *context.Context, repo *string, commit *string, linkURL *string, status *string, message *string) error { +panic("implement me") -// func (c *GithubClientImpl) SetStatus(ctx *context.Context, repo *string, commit *string, linkURL *string, status *string, message *string) error { // if !utils.ValidateHTTPFormat(*linkURL) { // return fmt.Errorf("invalid linkURL") // } @@ -331,9 +343,11 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // log.Printf("successfully set status on repo:%s commit: %s to status: %s\n", *repo, *commit, *status) // return nil -// } +} + +func (c *GitlabClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) error { + panic("implement me") -// func (c *GithubClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) error { // if c.cfg.OrgLevelWebhook && hook.RepoName != nil { // return fmt.Errorf("trying to ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) // } @@ -358,9 +372,11 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // } // return nil -// } +} + +func (c *GitlabClientImpl) refToSHA(ctx *context.Context, ref string, repo string) (*string, error) { +panic("implement me") -// func (c *GithubClientImpl) refToSHA(ctx *context.Context, ref string, repo string) (*string, error) { // respSHA, resp, err := c.client.Repositories.GetCommitSHA1(*ctx, c.cfg.OrgName, repo, ref, "") // if err != nil { // return nil, err @@ -372,12 +388,14 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { // log.Printf("resolved ref: %s to SHA: %s", ref, respSHA) // return &respSHA, nil -// } +} + +func (c *GitlabClientImpl) extractLabelNames(labels []*github.Label) []string { + panic("implement me") -// func (c *GithubClientImpl) extractLabelNames(labels []*github.Label) []string { // var returnLabelsList []string // for _, label := range labels { // returnLabelsList = append(returnLabelsList, *label.Name) // } // return returnLabelsList -// } +} From ec33703998b40e1427cbb28099d012f69a24bdad Mon Sep 17 00:00:00 2001 From: omri assa Date: Wed, 12 Jun 2024 22:58:31 +0300 Subject: [PATCH 03/20] fix: ListFiles complete --- pkg/git_provider/gitlab.go | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 95ef769..3bb46b3 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -51,24 +51,27 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { -// var files []string + var files []string + opt := &gitlab.ListTreeOptions{ + Ref: &branch, + Path: &path,} + + dirFiles, resp, err := c.client.Repositories.ListTree(repo, opt) -// opt := &github.RepositoryContentGetOptions{Ref: branch} -// _, directoryContent, resp, err := c.client.Repositories.GetContents(*ctx, c.cfg.GitProviderConfig.OrgName, repo, path, opt) -// if err != nil { -// return nil, err -// } -// if resp.StatusCode != 200 { -// return nil, fmt.Errorf("github provider returned %d: failed to get contents of %s/%s%s", resp.StatusCode, repo, branch, path) -// } -// if directoryContent == nil { -// return nil, nil -// } -// for _, file := range directoryContent { -// files = append(files, file.GetName()) -// } -// return files, nil - panic("implement me") + + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("gitlab provider returned %d: failed to get contents of %s/%s%s", resp.StatusCode, repo, branch, path) + } + if files == nil { + return nil, nil + } + for _, file := range dirFiles { + files = append(files, file.Name) + } + return files, nil } func (c *GitlabClientImpl) GetFile(ctx *context.Context, repo string, branch string, path string) (*CommitFile, error) { From f4caea511057db0c761571e62e366344e3f7cb60 Mon Sep 17 00:00:00 2001 From: omri assa Date: Wed, 12 Jun 2024 23:15:03 +0300 Subject: [PATCH 04/20] fix: get File and getFiles --- pkg/git_provider/gitlab.go | 73 ++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 3bb46b3..70bda28 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -75,50 +75,45 @@ func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch s } func (c *GitlabClientImpl) GetFile(ctx *context.Context, repo string, branch string, path string) (*CommitFile, error) { -// var commitFile CommitFile + var commitFile CommitFile -// opt := &github.RepositoryContentGetOptions{Ref: branch} -// fileContent, _, resp, err := c.client.Repositories.GetContents(*ctx, c.cfg.GitProviderConfig.OrgName, repo, path, opt) -// if err != nil { -// return &commitFile, err -// } -// if resp.StatusCode == 404 { -// log.Printf("File %s not found in repo %s branch %s", path, repo, branch) -// return nil, nil -// } -// if resp.StatusCode != 200 { -// return &commitFile, err -// } -// if fileContent == nil { -// return &commitFile, nil -// } -// filePath := fileContent.GetPath() -// commitFile.Path = &filePath -// fileContentString, err := fileContent.GetContent() -// if err != nil { -// return &commitFile, err -// } -// commitFile.Content = &fileContentString + opt := &gitlab.GetFileOptions{Ref: &branch,} + fileContent, resp, err := c.client.RepositoryFiles.GetFile(repo, path,opt) + if err != nil { + return &commitFile, err + } + if resp.StatusCode == 404 { + log.Printf("File %s not found in repo %s branch %s", path, repo, branch) + return nil, nil + } + if resp.StatusCode != 200 { + return &commitFile, err + } + if fileContent == nil { + return &commitFile, nil + } + filePath := fileContent.FilePath + commitFile.Path = &filePath + fileContentString := fileContent.Content + commitFile.Content = &fileContentString -// return &commitFile, nil - panic("implement me") + return &commitFile, nil } func (c *GitlabClientImpl) GetFiles(ctx *context.Context, repo string, branch string, paths []string) ([]*CommitFile, error) { -// var commitFiles []*CommitFile -// for _, path := range paths { -// file, err := c.GetFile(ctx, repo, branch, path) -// if err != nil { -// return nil, err -// } -// if file == nil { -// log.Printf("file %s not found in repo %s branch %s", path, repo, branch) -// continue -// } -// commitFiles = append(commitFiles, file) -// } -// return commitFiles, nil -panic("implement me") + var commitFiles []*CommitFile + for _, path := range paths { + file, err := c.GetFile(ctx, repo, branch, path) + if err != nil { + return nil, err + } + if file == nil { + log.Printf("file %s not found in repo %s branch %s", path, repo, branch) + continue + } + commitFiles = append(commitFiles, file) + } + return commitFiles, nil } func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*HookWithStatus, error) { From f88641d7452dd42e25e756e0d24811c435a11a5d Mon Sep 17 00:00:00 2001 From: omri assa Date: Thu, 13 Jun 2024 00:14:23 +0300 Subject: [PATCH 05/20] fix: SetStatus + PinkHook still need to do tests on them, not sure if working --- pkg/git_provider/gitlab.go | 113 ++++++++++++++----------------------- 1 file changed, 42 insertions(+), 71 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 70bda28..4c29e30 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -6,8 +6,8 @@ import ( "log" "net/http" - "github.com/google/go-github/v52/github" "github.com/quickube/piper/pkg/conf" + "github.com/quickube/piper/pkg/utils" "github.com/xanzy/go-gitlab" ) @@ -317,83 +317,54 @@ panic("implement me") } func (c *GitlabClientImpl) SetStatus(ctx *context.Context, repo *string, commit *string, linkURL *string, status *string, message *string) error { -panic("implement me") - -// if !utils.ValidateHTTPFormat(*linkURL) { -// return fmt.Errorf("invalid linkURL") -// } -// repoStatus := &github.RepoStatus{ -// State: status, // pending, success, error, or failure. -// TargetURL: linkURL, -// Description: utils.SPtr(fmt.Sprintf("Workflow %s %s", *status, *message)), -// Context: utils.SPtr("Piper/ArgoWorkflows"), -// AvatarURL: utils.SPtr("https://argoproj.github.io/argo-workflows/assets/logo.png"), -// } + if !utils.ValidateHTTPFormat(*linkURL) { + return fmt.Errorf("invalid linkURL") + } -// _, resp, err := c.client.Repositories.CreateStatus(*ctx, c.cfg.OrgName, *repo, *commit, repoStatus) -// if err != nil { -// return err -// } + repoStatus := &gitlab.SetCommitStatusOptions{ + State: gitlab.BuildStateValue(*status), // pending, success, error, or failure. + Ref: commit, + TargetURL: linkURL, + Description: gitlab.Ptr(fmt.Sprintf("Workflow %s %s", *status, *message)), + Context: gitlab.Ptr("Piper/ArgoWorkflows"), + } + + _, resp, err := c.client.Commits.SetCommitStatus(*repo, *commit, repoStatus) + if err != nil { + return err + } -// if resp.StatusCode != http.StatusCreated { -// return fmt.Errorf("failed to set status on repo:%s, commit:%s, API call returned %d", *repo, *commit, resp.StatusCode) -// } + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("failed to set status on repo:%s, commit:%s, API call returned %d", *repo, *commit, resp.StatusCode) + } -// log.Printf("successfully set status on repo:%s commit: %s to status: %s\n", *repo, *commit, *status) -// return nil + log.Printf("successfully set status on repo:%s commit: %s to status: %s\n", *repo, *commit, *status) + return nil } func (c *GitlabClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) error { - panic("implement me") - -// if c.cfg.OrgLevelWebhook && hook.RepoName != nil { -// return fmt.Errorf("trying to ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) -// } -// if hook.RepoName == nil { -// resp, err := c.client.Organizations.PingHook(*ctx, c.cfg.OrgName, hook.HookID) -// if err != nil { -// return err -// } - -// if resp.StatusCode == http.StatusNotFound { -// return fmt.Errorf("unable to find organization webhook for hookID: %d", hook.HookID) -// } -// } else { -// resp, err := c.client.Repositories.PingHook(*ctx, c.cfg.GitProviderConfig.OrgName, *hook.RepoName, hook.HookID) -// if err != nil { -// return err -// } - -// if resp.StatusCode == http.StatusNotFound { -// return fmt.Errorf("unable to find repo webhook for repo:%s hookID: %d", *hook.RepoName, hook.HookID) -// } -// } - -// return nil -} - -func (c *GitlabClientImpl) refToSHA(ctx *context.Context, ref string, repo string) (*string, error) { -panic("implement me") - -// respSHA, resp, err := c.client.Repositories.GetCommitSHA1(*ctx, c.cfg.OrgName, repo, ref, "") -// if err != nil { -// return nil, err -// } - -// if resp.StatusCode != http.StatusOK { -// return nil, fmt.Errorf("failed to set status on repo:%s, commit:%s, API call returned %d", repo, ref, resp.StatusCode) -// } + if c.cfg.OrgLevelWebhook && hook.RepoName != nil { + return fmt.Errorf("trying to ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) + } + if hook.RepoName == nil { + _,resp, err := c.client.Groups.GetGroupHook(c.cfg.OrgName,int(hook.HookID), nil) + if err != nil { + return err + } -// log.Printf("resolved ref: %s to SHA: %s", ref, respSHA) -// return &respSHA, nil -} + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("unable to find organization webhook for hookID: %d", hook.HookID) + } + } else { + _,resp, err := c.client.Projects.GetProjectHook(hook.RepoName, int(hook.HookID), nil) + if err != nil { + return err + } -func (c *GitlabClientImpl) extractLabelNames(labels []*github.Label) []string { - panic("implement me") + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("unable to find repo webhook for repo:%s hookID: %d", *hook.RepoName, hook.HookID) + } + } -// var returnLabelsList []string -// for _, label := range labels { -// returnLabelsList = append(returnLabelsList, *label.Name) -// } -// return returnLabelsList + return nil } From 708d710d5b8d9f0e0e2b27c560ffe67765c4be96 Mon Sep 17 00:00:00 2001 From: omri assa Date: Fri, 14 Jun 2024 17:27:30 +0300 Subject: [PATCH 06/20] fix: set and del webhook done --- pkg/git_provider/github.go | 1 + pkg/git_provider/gitlab.go | 219 ++++++++++++++++--------------- pkg/git_provider/gitlab_utils.go | 42 ++++++ 3 files changed, 157 insertions(+), 105 deletions(-) diff --git a/pkg/git_provider/github.go b/pkg/git_provider/github.go index ec9e290..72467de 100644 --- a/pkg/git_provider/github.go +++ b/pkg/git_provider/github.go @@ -22,6 +22,7 @@ func NewGithubClient(cfg *conf.GlobalConfig) (Client, error) { ctx := context.Background() client := github.NewTokenClient(ctx, cfg.GitProviderConfig.Token) + err := ValidatePermissions(ctx, client, cfg) if err != nil { return nil, fmt.Errorf("failed to validate permissions: %v", err) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 4c29e30..00607a7 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -49,7 +49,6 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { }, err } - func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { var files []string opt := &gitlab.ListTreeOptions{ @@ -117,120 +116,130 @@ func (c *GitlabClientImpl) GetFiles(ctx *context.Context, repo string, branch st } func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*HookWithStatus, error) { -// if c.cfg.OrgLevelWebhook && repo != nil { -// return nil, fmt.Errorf("trying to set repo scope. repo: %s", *repo) -// } - -// hookConf := &github.Hook{ -// Config: map[string]interface{}{ -// "url": c.cfg.GitProviderConfig.WebhookURL, -// "content_type": "json", -// "secret": c.cfg.GitProviderConfig.WebhookSecret, -// }, -// Events: []string{"push", "pull_request", "create", "release"}, -// Active: github.Bool(true), - // } - -// if repo == nil { -// respHook, ok := isOrgWebhookEnabled(*ctx, c) -// if !ok { -// createdHook, resp, err := c.client.Organizations.CreateHook( -// *ctx, -// c.cfg.GitProviderConfig.OrgName, -// hookConf, -// ) -// if err != nil { -// return nil, err -// } -// if resp.StatusCode != 201 { -// return nil, fmt.Errorf("failed to create org level webhhok, API returned %d", resp.StatusCode) -// } -// log.Printf("edited webhook of type %s for %s name: %s\n", createdHook.GetType(), c.cfg.GitProviderConfig.OrgName, createdHook.Config["url"]) -// hookID := createdHook.GetID() -// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil -// } else { -// updatedHook, resp, err := c.client.Organizations.EditHook( -// *ctx, -// c.cfg.GitProviderConfig.OrgName, -// respHook.GetID(), -// hookConf, -// ) -// if err != nil { -// return nil, err -// } -// if resp.StatusCode != http.StatusOK { -// return nil, fmt.Errorf( -// "failed to update org level webhhok for %s, API returned %d", -// c.cfg.GitProviderConfig.OrgName, -// resp.StatusCode, -// ) -// } -// log.Printf("edited webhook of type %s for %s: %s\n", updatedHook.GetType(), c.cfg.GitProviderConfig.OrgName, updatedHook.Config["url"]) -// hookID := updatedHook.GetID() -// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil -// } -// } else { -// respHook, ok := isRepoWebhookEnabled(*ctx, c, *repo) -// if !ok { -// createdHook, resp, err := c.client.Repositories.CreateHook(*ctx, c.cfg.GitProviderConfig.OrgName, *repo, hookConf) -// if err != nil { -// return nil, err -// } - -// if resp.StatusCode != 201 { -// return nil, fmt.Errorf("failed to create repo level webhhok for %s, API returned %d", *repo, resp.StatusCode) -// } -// log.Printf("created webhook of type %s for %s: %s\n", createdHook.GetType(), *repo, createdHook.Config["url"]) -// hookID := createdHook.GetID() -// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil -// } else { -// updatedHook, resp, err := c.client.Repositories.EditHook(*ctx, c.cfg.GitProviderConfig.OrgName, *repo, respHook.GetID(), hookConf) -// if err != nil { -// return nil, err -// } -// if resp.StatusCode != http.StatusOK { -// return nil, fmt.Errorf("failed to update repo level webhhok for %s, API returned %d", *repo, resp.StatusCode) -// } -// log.Printf("edited webhook of type %s for %s: %s\n", updatedHook.GetType(), *repo, updatedHook.Config["url"]) -// hookID := updatedHook.GetID() -// return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil -// } + if c.cfg.OrgLevelWebhook && repo != nil { + return nil, fmt.Errorf("trying to set repo scope. repo: %s", *repo) + } + var gitlabHook gitlab.Hook + + if repo == nil { + respHook, ok := isGroupWebhookEnabled(c) + + if !ok { + groupHookOptions := gitlab.AddGroupHookOptions{ + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + MergeRequestsEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), + } + + gitlabHook, resp, err := c.client.Groups.AddGroupHook(c.cfg.GitProviderConfig.OrgName, &groupHookOptions) + if err != nil { + return nil, err + } + if resp.StatusCode != 201 { + return nil, fmt.Errorf("failed to create group level webhhok, API returned %d", resp.StatusCode) + } + log.Printf("added webhook for %s name: %s\n", c.cfg.GitProviderConfig.OrgName, gitlabHook.URL) + } else { + editedGroupHookOpt := gitlab.EditGroupHookOptions{ + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + MergeRequestsEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), + } + gitlabHook, resp, err := c.client.Groups.EditGroupHook( + c.cfg.GitProviderConfig.OrgName, + respHook.ID, + &editedGroupHookOpt, + ) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf( + "failed to update group level webhook for %s, API returned %d", + c.cfg.GitProviderConfig.OrgName, + resp.StatusCode, + ) + } + log.Printf("edited webhook for %s: %s\n", c.cfg.GitProviderConfig.OrgName, gitlabHook.URL) + } + } else { + respHook, ok := isProjectWebhookEnabled(*ctx, c, *repo) + if !ok { + addProjectHookOpts := gitlab.AddProjectHookOptions{ + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + MergeRequestsEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), + } + + gitlabHook, resp, err := c.client.Projects.AddProjectHook(repo, &addProjectHookOpts) + if err != nil { + return nil, err + } + if resp.StatusCode != 201 { + return nil, fmt.Errorf("failed to create repo level webhhok for %s, API returned %d", *repo, resp.StatusCode) + } + log.Printf("created webhook for %s: %s\n", *repo, gitlabHook.URL) + } else { + + editProjectHookOpts := gitlab.EditProjectHookOptions{ + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + MergeRequestsEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), + } + gitlabHook, resp, err := c.client.Projects.EditProjectHook(repo, respHook.ID, &editProjectHookOpts) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to update repo level webhhok for %s, API returned %d", *repo, resp.StatusCode) + } + log.Printf("edited webhook for %s: %s\n", *repo, gitlabHook.URL) + } -// } -panic("implement me") + } + hookID := int64(gitlabHook.ID) + return &HookWithStatus{HookID: hookID, HealthStatus: true, RepoName: repo}, nil } func (c *GitlabClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStatus) error { -// if hook.RepoName == nil { - -// resp, err := c.client.Organizations.DeleteHook(*ctx, c.cfg.GitProviderConfig.OrgName, hook.HookID) - -// if err != nil { -// return err -// } - -// if resp.StatusCode != 204 { -// return fmt.Errorf("failed to delete org level webhhok, API call returned %d", resp.StatusCode) -// } -// log.Printf("removed org webhook, hookID :%d\n", hook.HookID) // INFO -// } else { -// resp, err := c.client.Repositories.DeleteHook(*ctx, c.cfg.GitProviderConfig.OrgName, *hook.RepoName, hook.HookID) + if hook.RepoName == nil { + resp, err := c.client.Groups.DeleteGroupHook( c.cfg.GitProviderConfig.OrgName, int(hook.HookID)) + if err != nil { + return err + } -// if err != nil { -// return fmt.Errorf("failed to delete repo level webhhok for %s, API call returned %d. %s", *hook.RepoName, resp.StatusCode, err) -// } + if resp.StatusCode != 204 { + return fmt.Errorf("failed to delete group level webhhok, API call returned %d", resp.StatusCode) + } + log.Printf("removed group webhook, hookID :%d\n", hook.HookID) + } else { + resp, err := c.client.Projects.DeleteProjectHook(*hook.RepoName, int(hook.HookID)) -// if resp.StatusCode != 204 { -// return fmt.Errorf("failed to delete repo level webhhok for %s, API call returned %d", *hook.RepoName, resp.StatusCode) -// } -// log.Printf("removed repo webhook, repo:%s hookID :%d\n", *hook.RepoName, hook.HookID) // INFO -// } + if err != nil { + return fmt.Errorf("failed to delete project level webhhok for %s, API call returned %d. %s", *hook.RepoName, resp.StatusCode, err) + } -// return nil -panic("implement me") + if resp.StatusCode != 204 { + return fmt.Errorf("failed to delete project level webhhok for %s, API call returned %d", *hook.RepoName, resp.StatusCode) + } + log.Printf("removed project webhook, project:%s hookID :%d\n", *hook.RepoName, hook.HookID) // INFO + } + return nil } func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Request, secret []byte) (*WebhookPayload, error) { diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index b80f5c4..61faeac 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -70,4 +70,46 @@ func GetGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, erro scopes = strings.ReplaceAll(scopes, " ", "") return strings.Split(scopes, ","), nil +} + +func isGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { + emptyHook := gitlab.GroupHook{} + hooks, resp, err := c.client.Groups.ListGroupHooks(c.cfg.GitProviderConfig.OrgName, nil) + if err != nil { + return &emptyHook, false + } + if resp.StatusCode != 200 { + return &emptyHook, false + } + if len(hooks) == 0 { + return &emptyHook, false + } + for _, hook := range hooks { + if hook.AlertStatus == "triggered" && hook.URL == c.cfg.GitProviderConfig.WebhookURL { + return hook, true + } + } + return &emptyHook, false +} + +func isProjectWebhookEnabled(c *GitlabClientImpl, repo string) (*gitlab.ProjectHook, bool) { + emptyHook := gitlab.ProjectHook{} + hooks, resp, err := c.client.Projects.ListProjectHooks(repo, nil) + if err != nil { + return &emptyHook, false + } + if resp.StatusCode != 200 { + return &emptyHook, false + } + if len(hooks) == 0 { + return &emptyHook, false + } + + for _, hook := range hooks { + if hook.URL == c.cfg.GitProviderConfig.WebhookURL { + return hook, true + } + } + + return &emptyHook, false } \ No newline at end of file From 921472acb9cfc539dad57f07201c94d669f48970 Mon Sep 17 00:00:00 2001 From: omri assa Date: Fri, 14 Jun 2024 17:29:43 +0300 Subject: [PATCH 07/20] fix: small change setWebhook --- pkg/git_provider/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 00607a7..1a1e851 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -169,7 +169,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook log.Printf("edited webhook for %s: %s\n", c.cfg.GitProviderConfig.OrgName, gitlabHook.URL) } } else { - respHook, ok := isProjectWebhookEnabled(*ctx, c, *repo) + respHook, ok := isProjectWebhookEnabled(c, *repo) if !ok { addProjectHookOpts := gitlab.AddProjectHookOptions{ URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), From 7261c2632f0fb15998566a871735479a9ccaa80f Mon Sep 17 00:00:00 2001 From: omri assa Date: Tue, 18 Jun 2024 23:01:08 +0300 Subject: [PATCH 08/20] fix: done handlePaylod will now start tests --- pkg/git_provider/gitlab.go | 135 +++++++++++++------------------ pkg/git_provider/gitlab_utils.go | 40 ++++++++- 2 files changed, 95 insertions(+), 80 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 1a1e851..eb97e4d 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/http" + "strings" "github.com/quickube/piper/pkg/conf" "github.com/quickube/piper/pkg/utils" @@ -243,86 +244,62 @@ func (c *GitlabClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStat } func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Request, secret []byte) (*WebhookPayload, error) { -// var webhookPayload *WebhookPayload - -// payload, err := github.ValidatePayload(request, secret) -// if err != nil { -// return nil, err -// } - -// event, err := github.ParseWebHook(github.WebHookType(request), payload) -// if err != nil { -// return nil, err -// } - -// switch e := event.(type) { -// case *github.PingEvent: -// webhookPayload = &WebhookPayload{ -// Event: "ping", -// Repo: e.GetRepo().GetFullName(), -// HookID: e.GetHookID(), -// OwnerID: e.GetSender().GetID(), -// } -// case *github.PushEvent: -// webhookPayload = &WebhookPayload{ -// Event: "push", -// Action: e.GetAction(), -// Repo: e.GetRepo().GetName(), -// Branch: strings.TrimPrefix(e.GetRef(), "refs/heads/"), -// Commit: e.GetHeadCommit().GetID(), -// User: e.GetSender().GetLogin(), -// UserEmail: e.GetHeadCommit().GetAuthor().GetEmail(), -// OwnerID: e.GetSender().GetID(), -// } -// case *github.PullRequestEvent: -// webhookPayload = &WebhookPayload{ -// Event: "pull_request", -// Action: e.GetAction(), -// Repo: e.GetRepo().GetName(), -// Branch: e.GetPullRequest().GetHead().GetRef(), -// Commit: e.GetPullRequest().GetHead().GetSHA(), -// User: e.GetPullRequest().GetUser().GetLogin(), -// UserEmail: e.GetSender().GetEmail(), // e.GetPullRequest().GetUser().GetEmail() Not working. GitHub missing email for PR events in payload. -// PullRequestTitle: e.GetPullRequest().GetTitle(), -// PullRequestURL: e.GetPullRequest().GetHTMLURL(), -// DestBranch: e.GetPullRequest().GetBase().GetRef(), -// Labels: c.extractLabelNames(e.GetPullRequest().Labels), -// OwnerID: e.GetSender().GetID(), -// } -// case *github.CreateEvent: -// webhookPayload = &WebhookPayload{ -// Event: "create", -// Action: e.GetRefType(), // Possible values are: "repository", "branch", "tag". -// Repo: e.GetRepo().GetName(), -// Branch: e.GetRef(), -// Commit: e.GetRef(), -// User: e.GetSender().GetLogin(), -// UserEmail: e.GetSender().GetEmail(), -// OwnerID: e.GetSender().GetID(), -// } -// case *github.ReleaseEvent: -// commitSHA, _err := c.refToSHA(ctx, e.GetRelease().GetName(), e.GetRepo().GetName()) -// if _err != nil { -// return webhookPayload, _err -// } -// webhookPayload = &WebhookPayload{ -// Event: "release", -// Action: e.GetAction(), // "created", "edited", "deleted", or "prereleased". -// Repo: e.GetRepo().GetName(), -// Branch: e.GetRelease().GetTagName(), -// Commit: *commitSHA, -// User: e.GetSender().GetLogin(), -// UserEmail: e.GetSender().GetEmail(), -// OwnerID: e.GetSender().GetID(), -// } -// } - -// if c.cfg.EnforceOrgBelonging && (webhookPayload.OwnerID == 0 || webhookPayload.OwnerID != c.cfg.OrgID) { -// return nil, fmt.Errorf("webhook send from non organizational member") -// } -// return webhookPayload, nil -panic("implement me") + var webhookPayload *WebhookPayload + payload, err := validatePayload(request, secret) + if err != nil { + return nil, err + } + + event, err := gitlab.ParseWebhook(gitlab.WebhookEventType(request), payload) + if err != nil { + return nil, err + } + + + switch e := event.(type) { + case gitlab.PushEvent: + webhookPayload = &WebhookPayload{ + Event: "push", + Action: e.EventName, + Repo: e.Project.Name, + Branch: strings.TrimPrefix(e.Ref, "refs/heads/"), + Commit: e.CheckoutSHA, + User: e.UserName, + UserEmail: e.UserEmail, + OwnerID: int64(e.UserID), + } + case gitlab.MergeEvent: + webhookPayload = &WebhookPayload{ + Event: "pull_request", + Action: e.ObjectAttributes.Action, + Repo: e.Repository.Name, + Branch: e.ObjectAttributes.SourceBranch, + Commit: e.ObjectAttributes.LastCommit.ID, + User: e.User.Name, + UserEmail: e.User.Email, + PullRequestTitle: e.ObjectAttributes.Title, + PullRequestURL: e.ObjectAttributes.URL, + DestBranch: e.ObjectAttributes.TargetBranch, + Labels: extractLabelsId(e.Labels), + OwnerID: int64(e.User.ID), + } + case gitlab.ReleaseEvent: + webhookPayload = &WebhookPayload{ + Event: "release", + Action: e.Action, // "create" | "update" | "delete" + Repo: e.Project.Name, + Branch: e.Tag, + Commit: e.Commit.ID, + User: e.Commit.Author.Name, + UserEmail: e.Commit.Author.Email, + } + } + + if c.cfg.EnforceOrgBelonging && (webhookPayload.OwnerID == 0 || webhookPayload.OwnerID != c.cfg.OrgID) { + return nil, fmt.Errorf("webhook send from non organizational member") + } + return webhookPayload, nil } func (c *GitlabClientImpl) SetStatus(ctx *context.Context, repo *string, commit *string, linkURL *string, status *string, message *string) error { diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index 61faeac..c332bb0 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -1,7 +1,11 @@ package git_provider import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" "fmt" + "io" "log" "net/http" "strings" @@ -112,4 +116,38 @@ func isProjectWebhookEnabled(c *GitlabClientImpl, repo string) (*gitlab.ProjectH } return &emptyHook, false -} \ No newline at end of file +} + +func extractLabelsId(labels []*gitlab.EventLabel) []string { + var returnLabelsList []string + for _, label := range labels { + returnLabelsList = append(returnLabelsList, fmt.Sprint(label.ID)) + } + return returnLabelsList +} + +func validatePayload(r *http.Request, secret []byte) ([]byte, error){ + payload, err := io.ReadAll(r.Body) + if err != nil { + return nil, fmt.Errorf("error reading request body: %v", err) + } + + // Get GitLab signature from headers + gitlabSignature := r.Header.Get("X-Gitlab-Token") + if gitlabSignature == "" { + return nil, fmt.Errorf("no GitLab signature found in headers") + } + + h := hmac.New(sha256.New, secret) + _, err = h.Write(payload) + if err != nil { + return nil, fmt.Errorf("error computing HMAC: %v", err) + } + expectedMAC := hex.EncodeToString(h.Sum(nil)) + + isEquall := hmac.Equal([]byte(gitlabSignature), []byte(expectedMAC)) + if !isEquall { + return nil, fmt.Errorf("secret not correct") + } + return payload, nil +} From 583d898bc84321af30fc072273b4c5f6faa98721 Mon Sep 17 00:00:00 2001 From: omri assa Date: Wed, 19 Jun 2024 13:52:28 +0300 Subject: [PATCH 09/20] fix: pr review changes and fixed scopes --- pkg/git_provider/gitlab.go | 20 ++++------------- pkg/git_provider/gitlab_utils.go | 38 ++++++++++++++------------------ 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index eb97e4d..4cb4437 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -23,7 +23,7 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { client, err := gitlab.NewClient(cfg.GitProviderConfig.Token) if err != nil { - log.Fatalf("Failed to create client: %v", err) + return nil, err } err = ValidateGitlabPermissions(ctx, client, cfg) @@ -65,9 +65,6 @@ func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch s if resp.StatusCode != 200 { return nil, fmt.Errorf("gitlab provider returned %d: failed to get contents of %s/%s%s", resp.StatusCode, repo, branch, path) } - if files == nil { - return nil, nil - } for _, file := range dirFiles { files = append(files, file.Name) } @@ -82,20 +79,11 @@ func (c *GitlabClientImpl) GetFile(ctx *context.Context, repo string, branch str if err != nil { return &commitFile, err } - if resp.StatusCode == 404 { - log.Printf("File %s not found in repo %s branch %s", path, repo, branch) - return nil, nil - } if resp.StatusCode != 200 { return &commitFile, err } - if fileContent == nil { - return &commitFile, nil - } - filePath := fileContent.FilePath - commitFile.Path = &filePath - fileContentString := fileContent.Content - commitFile.Content = &fileContentString + commitFile.Path = &fileContent.FilePath + commitFile.Content = &fileContent.Content return &commitFile, nil } @@ -271,7 +259,7 @@ func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Req } case gitlab.MergeEvent: webhookPayload = &WebhookPayload{ - Event: "pull_request", + Event: "merge_request", Action: e.ObjectAttributes.Action, Repo: e.Repository.Name, Branch: e.ObjectAttributes.SourceBranch, diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index c332bb0..ad3f88f 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -19,9 +19,8 @@ import ( func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg *conf.GlobalConfig) error { - orgScopes := []string{"admin:org_hook"} - repoAdminScopes := []string{"admin:repo_hook"} - repoGranularScopes := []string{"write:repo_hook", "read:repo_hook"} + repoAdminScopes := []string{"api"} + repoGranularScopes := []string{"write_repository", "read_api"} scopes, err := GetGitlabScopes(ctx, client) @@ -32,13 +31,6 @@ func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg * return fmt.Errorf("permissions error: no scopes found for the gitlab client") } - if cfg.GitProviderConfig.OrgLevelWebhook { - if utils.ListContains(orgScopes, scopes) { - return nil - } - return fmt.Errorf("permissions error: %v is not a valid scope for the org level permissions", scopes) - } - if utils.ListContains(repoAdminScopes, scopes) { return nil } @@ -46,20 +38,24 @@ func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg * return nil } - return fmt.Errorf("permissions error: %v is not a valid scope for the repo level permissions", scopes) + return fmt.Errorf("permissions error: %v is not a valid scope for the project level permissions", scopes) } func GetGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, error) { - req, err := retryablehttp.NewRequest("GET", "https://gitlab.com/api/v4/user", nil) + req, err := retryablehttp.NewRequest("GET", "https://gitlab.com/api/v4/personal_access_tokens", nil) if err != nil { log.Fatalf("Failed to create request: %v", err) + return nil, err } - - resp, err := client.Do(req, nil) + var tokens []struct { + ID int `json:"id"` + Name string `json:"name"` + Scopes []string `json:"scopes"` + } + resp, err := client.Do(req, &tokens) if err != nil { log.Fatalf("Failed to perform request: %v", err) return nil, err - } defer resp.Body.Close() @@ -68,12 +64,10 @@ func GetGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, erro log.Fatalf("Failed to get user: %v", resp.Status) return nil, err } + scopes := tokens[0].Scopes + fmt.Println("Gitlab Token Scopes are:", scopes) - scopes := resp.Header.Get("X-OAuth-Scopes") - fmt.Println("Github Token Scopes are:", scopes) - - scopes = strings.ReplaceAll(scopes, " ", "") - return strings.Split(scopes, ","), nil + return scopes, nil } func isGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { @@ -96,9 +90,9 @@ func isGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { return &emptyHook, false } -func isProjectWebhookEnabled(c *GitlabClientImpl, repo string) (*gitlab.ProjectHook, bool) { +func isProjectWebhookEnabled(c *GitlabClientImpl, project string) (*gitlab.ProjectHook, bool) { emptyHook := gitlab.ProjectHook{} - hooks, resp, err := c.client.Projects.ListProjectHooks(repo, nil) + hooks, resp, err := c.client.Projects.ListProjectHooks(project, nil) if err != nil { return &emptyHook, false } From 6d66c9fadd2bcbc5352f0efbf69d33c6d068599b Mon Sep 17 00:00:00 2001 From: omri assa Date: Fri, 28 Jun 2024 00:22:22 +0300 Subject: [PATCH 10/20] fix: first test done --- pkg/git_provider/gitlab.go | 2 +- pkg/git_provider/gitlab_utils.go | 38 ++++----- pkg/git_provider/gitlab_utils_test.go | 115 ++++++++++++++++++++++++++ pkg/git_provider/test_utils.go | 31 ++++++- 4 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 pkg/git_provider/gitlab_utils_test.go diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 4cb4437..9b833c8 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -111,7 +111,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook var gitlabHook gitlab.Hook if repo == nil { - respHook, ok := isGroupWebhookEnabled(c) + respHook, ok := IsGroupWebhookEnabled(c) if !ok { groupHookOptions := gitlab.AddGroupHookOptions{ diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index ad3f88f..c02a5ba 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -6,11 +6,8 @@ import ( "encoding/hex" "fmt" "io" - "log" "net/http" - "strings" - "github.com/hashicorp/go-retryablehttp" "github.com/quickube/piper/pkg/conf" "github.com/quickube/piper/pkg/utils" "github.com/xanzy/go-gitlab" @@ -42,35 +39,34 @@ func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg * } func GetGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, error) { - req, err := retryablehttp.NewRequest("GET", "https://gitlab.com/api/v4/personal_access_tokens", nil) - if err != nil { - log.Fatalf("Failed to create request: %v", err) + + user, resp,err := client.Users.CurrentUser() + fmt.Println(user.ID) + if err != nil{ return nil, err } - var tokens []struct { - ID int `json:"id"` - Name string `json:"name"` - Scopes []string `json:"scopes"` + if resp.StatusCode == 400 { + return nil, err } - resp, err := client.Do(req, &tokens) - if err != nil { - log.Fatalf("Failed to perform request: %v", err) + a := gitlab.ListPersonalAccessTokensOptions{ + UserID: &user.ID, + } + accessTokens, resp,err := client.PersonalAccessTokens.ListPersonalAccessTokens(&a) + fmt.Println(accessTokens) + if err != nil{ return nil, err } - defer resp.Body.Close() - - // Check for successful response - if resp.StatusCode != http.StatusOK { - log.Fatalf("Failed to get user: %v", resp.Status) + if resp.StatusCode == 400 { return nil, err } - scopes := tokens[0].Scopes + + scopes := accessTokens[0].Scopes fmt.Println("Gitlab Token Scopes are:", scopes) return scopes, nil } -func isGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { +func IsGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { emptyHook := gitlab.GroupHook{} hooks, resp, err := c.client.Groups.ListGroupHooks(c.cfg.GitProviderConfig.OrgName, nil) if err != nil { @@ -145,3 +141,5 @@ func validatePayload(r *http.Request, secret []byte) ([]byte, error){ } return payload, nil } + + diff --git a/pkg/git_provider/gitlab_utils_test.go b/pkg/git_provider/gitlab_utils_test.go new file mode 100644 index 0000000..9a0c94f --- /dev/null +++ b/pkg/git_provider/gitlab_utils_test.go @@ -0,0 +1,115 @@ +package git_provider + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "testing" + + "github.com/quickube/piper/pkg/conf" + assertion "github.com/stretchr/testify/assert" + "github.com/xanzy/go-gitlab" + "golang.org/x/net/context" +) + +// import ( +// "fmt" +// "net/http" +// "testing" + +// assertion "github.com/stretchr/testify/assert" +// "golang.org/x/net/context" +// ) + +func mockHTTPResponse(t *testing.T, w io.Writer, response interface{}) { + json.NewEncoder(w).Encode(response) +} + + +func TestValidateGitlabPermissions(t *testing.T){ + // + // Prepare + // + type testData = struct { + name string + scopes []string + raiseErr bool + } + var CurrentTest testData + mux, client := setupGitlab(t) + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "test", + RepoList: "test-repo1", + }, + }, + } + ctx := context.Background() + mux.HandleFunc("/api/v4/user", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + mockHTTPResponse(t, w, gitlab.User{ID:1234}) + }) + mux.HandleFunc("/api/v4/personal_access_tokens", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + mockHTTPResponse(t, w, []gitlab.PersonalAccessToken{{Scopes: CurrentTest.scopes,}}) + }) + // + // Execute + // + tests := []testData{ + {name:"validScope", scopes: []string{"api"}, raiseErr: false}, + {name:"invalidScope", scopes: []string{"invalid"}, raiseErr: true}, + } + for _, test := range tests { + CurrentTest = test + t.Run(test.name, func(t *testing.T) { + err := ValidateGitlabPermissions(ctx, c.client, c.cfg) + // + // Assert + // + assert := assertion.New(t) + if test.raiseErr{ + assert.NotNil(err) + }else{ + assert.Nil(err) + } + }) + } +} + +func TestIsGroupWebhookEnabled(t *testing.T){ + // + // Prepare + // + mux, client := setupGitlab(t) + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "group1", + RepoList: "test-repo1", + }, + }, + } + mux.HandleFunc("api/v4/groups/group1/hooks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Println("ding ding") + // mockHTTPResponse(t, w, gitlab.User{ID:1234}) + }) + // + // Execute + // + groupHook, isEnabled := IsGroupWebhookEnabled(&c) + // + // Assert + // + assert := assertion.New(t) + assert.Equal(isEnabled, false) + assert.NotEqual(string(groupHook.GroupID), "group1") +} + diff --git a/pkg/git_provider/test_utils.go b/pkg/git_provider/test_utils.go index 20c13b5..909032f 100644 --- a/pkg/git_provider/test_utils.go +++ b/pkg/git_provider/test_utils.go @@ -2,14 +2,17 @@ package git_provider import ( "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-github/v52/github" - "github.com/ktrysmt/go-bitbucket" "net/http" "net/http/httptest" "net/url" "os" "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-github/v52/github" + "github.com/ktrysmt/go-bitbucket" + "github.com/xanzy/go-gitlab" ) const ( @@ -88,3 +91,25 @@ func setupBitbucket() (client *bitbucket.Client, mux *http.ServeMux, serverURL s return client, mux, server.URL, server.Close } +func setupGitlab(t *testing.T) (*http.ServeMux, *gitlab.Client) { + // mux is the HTTP request multiplexer used with the test server. + mux := http.NewServeMux() + + // server is a test HTTP server used to provide mock API responses. + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + // client is the Gitlab client being tested. + client, err := gitlab.NewClient("", + gitlab.WithBaseURL(server.URL), + // Disable backoff to speed up tests that expect errors. + gitlab.WithCustomBackoff(func(_, _ time.Duration, _ int, _ *http.Response) time.Duration { + return 0 + }), + ) + if err != nil { + t.Fatalf("Failed to create client: %v", err) + } + + return mux, client +} \ No newline at end of file From 3a41c0a84ee2219d232555919ec18ebd5e3e17d2 Mon Sep 17 00:00:00 2001 From: omri assa Date: Fri, 28 Jun 2024 00:34:25 +0300 Subject: [PATCH 11/20] fix: some pr changes resolving --- pkg/conf/git_provider.go | 1 + pkg/git_provider/gitlab.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/conf/git_provider.go b/pkg/conf/git_provider.go index 9d6a0ad..b61937e 100644 --- a/pkg/conf/git_provider.go +++ b/pkg/conf/git_provider.go @@ -9,6 +9,7 @@ import ( type GitProviderConfig struct { Provider string `envconfig:"GIT_PROVIDER" required:"true"` Token string `envconfig:"GIT_TOKEN" required:"true"` + Url string `envconfig:"GIT_URL" required:"true"` OrgName string `envconfig:"GIT_ORG_NAME" required:"true"` OrgLevelWebhook bool `envconfig:"GIT_ORG_LEVEL_WEBHOOK" default:"false" required:"false"` RepoList string `envconfig:"GIT_WEBHOOK_REPO_LIST" required:"false"` diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 9b833c8..ca49ec3 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -21,7 +21,7 @@ type GitlabClientImpl struct { func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { ctx := context.Background() - client, err := gitlab.NewClient(cfg.GitProviderConfig.Token) + client, err := gitlab.NewClient(cfg.GitProviderConfig.Token, gitlab.WithBaseURL(cfg.GitProviderConfig.Url)) if err != nil { return nil, err } From c12832af8ee0ec91bc15c827e4f4c007d039f28f Mon Sep 17 00:00:00 2001 From: omri assa Date: Sat, 29 Jun 2024 12:53:50 +0300 Subject: [PATCH 12/20] docs: added provider and url to chart --- helm-chart/templates/deployment.yaml | 2 ++ helm-chart/values.yaml | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/helm-chart/templates/deployment.yaml b/helm-chart/templates/deployment.yaml index e26b84e..10959fa 100644 --- a/helm-chart/templates/deployment.yaml +++ b/helm-chart/templates/deployment.yaml @@ -100,6 +100,8 @@ spec: key: token - name: GIT_ORG_NAME value: {{ .Values.piper.gitProvider.organization.name | quote }} + - name: GIT_URL + value: {{ .Values.piper.gitProvider.url | quote }} - name: GIT_WEBHOOK_URL value: {{ .Values.piper.gitProvider.webhook.url | quote }} - name: GIT_WEBHOOK_SECRET diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index acf5809..53f96e0 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -4,7 +4,7 @@ # Map of Piper configurations. piper: gitProvider: - # -- Name of your git provider (github/bitbucket). + # -- Name of your git provider (github/bitbucket/gitlab). name: github # -- The token for authentication with the Git provider. # -- This will create a secret named -git-token and with the key 'token' @@ -13,6 +13,9 @@ piper: # -- Reference to existing token with 'token' key. # -- can be created with `kubectl create secret generic piper-git-token --from-literal=token=YOUR_TOKEN` existingSecret: #piper-git-token + # -- git provider url + # -- relevant when using gitlab self hosted + url: "" # Map of organization configurations. organization: # -- Name of your Git Organization (GitHub) or Workspace (Bitbucket) @@ -51,8 +54,9 @@ piper: existingSecret: #piper-argo-token # -- Whether create Workflow CRD or send direct commands to Argo Workflows server. crdCreation: true - - workflowsConfig: {} + + workflowsConfig: + {} # default: | # spec: # volumes: @@ -126,22 +130,21 @@ podAnnotations: {} # -- Security Context to set on the pod level podSecurityContext: - fsGroup: 1001 - runAsUser: 1001 - runAsGroup: 1001 + fsGroup: 1001 + runAsUser: 1001 + runAsGroup: 1001 # -- Security Context to set on the container level securityContext: runAsUser: 1001 capabilities: drop: - - ALL + - ALL readOnlyRootFilesystem: true runAsNonRoot: true - service: - # -- Sets the type of the Service + # -- Sets the type of the Service type: ClusterIP # -- Service port # For TLS mode change the port to 443 @@ -158,10 +161,11 @@ ingress: # -- Piper ingress class name className: "" # -- Piper ingress annotations - annotations: {} + annotations: + {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - + # -- Piper ingress hosts ## Hostnames must be provided if Ingress is enabled. hosts: @@ -215,4 +219,4 @@ volumes: [] volumeMounts: [] # -- Specify postStart and preStop lifecycle hooks for Piper container -lifecycle: {} \ No newline at end of file +lifecycle: {} From 462ad662ef1eabb853bc2c0469580d7bfa735282 Mon Sep 17 00:00:00 2001 From: omri assa Date: Sat, 29 Jun 2024 17:55:10 +0300 Subject: [PATCH 13/20] fix: done utils tests --- helm-chart/values.yaml | 4 +- pkg/conf/git_provider.go | 6 +-- pkg/git_provider/gitlab.go | 26 +++++----- pkg/git_provider/gitlab_utils.go | 25 +++++++--- pkg/git_provider/gitlab_utils_test.go | 68 ++++++++++++++++++++------- 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 53f96e0..7d1e21b 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -18,7 +18,7 @@ piper: url: "" # Map of organization configurations. organization: - # -- Name of your Git Organization (GitHub) or Workspace (Bitbucket) + # -- Name of your Git Organization (GitHub) / Workspace (Bitbucket) or Group (Gitlab) name: "" # Map of webhook configurations. webhook: @@ -34,7 +34,7 @@ piper: url: "" #https://piper.example.local/webhook # -- Whether config webhook on org level (GitHub) or at workspace level (Bitbucket - not supported yet) orgLevel: false - # -- (Github) Used of orgLevel=false, to configure webhook for each of the repos provided. + # -- (Github/Gitlab) Used of orgLevel=false, to configure webhook for each of the repos provided. repoList: [] # Map of Argo Workflows configurations. diff --git a/pkg/conf/git_provider.go b/pkg/conf/git_provider.go index b61937e..16bb76c 100644 --- a/pkg/conf/git_provider.go +++ b/pkg/conf/git_provider.go @@ -1,7 +1,7 @@ package conf import ( - "fmt" + "fmt" "github.com/kelseyhightower/envconfig" ) @@ -10,13 +10,13 @@ type GitProviderConfig struct { Provider string `envconfig:"GIT_PROVIDER" required:"true"` Token string `envconfig:"GIT_TOKEN" required:"true"` Url string `envconfig:"GIT_URL" required:"true"` - OrgName string `envconfig:"GIT_ORG_NAME" required:"true"` + OrgName string `envconfig:"GIT_ORG_NAME" required:"true"` OrgLevelWebhook bool `envconfig:"GIT_ORG_LEVEL_WEBHOOK" default:"false" required:"false"` RepoList string `envconfig:"GIT_WEBHOOK_REPO_LIST" required:"false"` WebhookURL string `envconfig:"GIT_WEBHOOK_URL" required:"false"` WebhookSecret string `envconfig:"GIT_WEBHOOK_SECRET" required:"false"` WebhookAutoCleanup bool `envconfig:"GIT_WEBHOOK_AUTO_CLEANUP" default:"false" required:"false"` - EnforceOrgBelonging bool `envconfig:"GIT_ENFORCE_ORG_BELONGING" default:"false" required:"false"` + EnforceOrgBelonging bool `envconfig:"GIT_ENFORCE_ORG_BELONGING" default:"false" required:"false"` OrgID int64 FullHealthCheck bool `envconfig:"GIT_FULL_HEALTH_CHECK" default:"false" required:"false"` } diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index ca49ec3..6dae546 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -3,6 +3,7 @@ package git_provider import ( "context" "fmt" + "io" "log" "net/http" "strings" @@ -31,7 +32,7 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { return nil, fmt.Errorf("failed to validate permissions: %v", err) } - group, resp, err := client.Groups.GetGroup(cfg.OrgName, nil) + group, resp, err := client.Groups.GetGroup(cfg.GitProviderConfig.OrgName, nil) if err != nil { return nil, fmt.Errorf("failed to get organization: %v", err) } @@ -40,14 +41,15 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { return nil, fmt.Errorf("failed to get organization data %s", resp.Status) } - cfg.OrgID = int64(group.ID) - - log.Printf("Org ID is: %d\n", cfg.OrgID) - - return &GitlabClientImpl{ + c := &GitlabClientImpl{ client: client, cfg: cfg, - }, err + } + + c.cfg.GitProviderConfig.OrgID = int64(group.ID) + log.Printf("Org ID is: %d\n", cfg.OrgID) + + return c, err } func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { @@ -158,7 +160,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook log.Printf("edited webhook for %s: %s\n", c.cfg.GitProviderConfig.OrgName, gitlabHook.URL) } } else { - respHook, ok := isProjectWebhookEnabled(c, *repo) + respHook, ok := IsProjectWebhookEnabled(c, *repo) if !ok { addProjectHookOpts := gitlab.AddProjectHookOptions{ URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), @@ -233,12 +235,10 @@ func (c *GitlabClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStat func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Request, secret []byte) (*WebhookPayload, error) { var webhookPayload *WebhookPayload - - payload, err := validatePayload(request, secret) + payload, err := io.ReadAll(request.Body) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading request body: %v", err) } - event, err := gitlab.ParseWebhook(gitlab.WebhookEventType(request), payload) if err != nil { return nil, err @@ -269,7 +269,7 @@ func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Req PullRequestTitle: e.ObjectAttributes.Title, PullRequestURL: e.ObjectAttributes.URL, DestBranch: e.ObjectAttributes.TargetBranch, - Labels: extractLabelsId(e.Labels), + Labels: ExtractLabelsId(e.Labels), OwnerID: int64(e.User.ID), } case gitlab.ReleaseEvent: diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index c02a5ba..bdcf9da 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "strings" "github.com/quickube/piper/pkg/conf" "github.com/quickube/piper/pkg/utils" @@ -19,7 +20,7 @@ func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg * repoAdminScopes := []string{"api"} repoGranularScopes := []string{"write_repository", "read_api"} - scopes, err := GetGitlabScopes(ctx, client) + scopes, err := getGitlabScopes(ctx, client) if err != nil { return fmt.Errorf("failed to get scopes: %v", err) @@ -38,7 +39,7 @@ func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg * return fmt.Errorf("permissions error: %v is not a valid scope for the project level permissions", scopes) } -func GetGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, error) { +func getGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, error) { user, resp,err := client.Users.CurrentUser() fmt.Println(user.ID) @@ -79,16 +80,17 @@ func IsGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { return &emptyHook, false } for _, hook := range hooks { - if hook.AlertStatus == "triggered" && hook.URL == c.cfg.GitProviderConfig.WebhookURL { + if hook.URL == c.cfg.GitProviderConfig.WebhookURL { return hook, true } } return &emptyHook, false } -func isProjectWebhookEnabled(c *GitlabClientImpl, project string) (*gitlab.ProjectHook, bool) { +func IsProjectWebhookEnabled(c *GitlabClientImpl, project string) (*gitlab.ProjectHook, bool) { emptyHook := gitlab.ProjectHook{} - hooks, resp, err := c.client.Projects.ListProjectHooks(project, nil) + projectFullName := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName ,project) + hooks, resp, err := c.client.Projects.ListProjectHooks(projectFullName, nil) if err != nil { return &emptyHook, false } @@ -108,7 +110,7 @@ func isProjectWebhookEnabled(c *GitlabClientImpl, project string) (*gitlab.Proje return &emptyHook, false } -func extractLabelsId(labels []*gitlab.EventLabel) []string { +func ExtractLabelsId(labels []*gitlab.EventLabel) []string { var returnLabelsList []string for _, label := range labels { returnLabelsList = append(returnLabelsList, fmt.Sprint(label.ID)) @@ -116,7 +118,7 @@ func extractLabelsId(labels []*gitlab.EventLabel) []string { return returnLabelsList } -func validatePayload(r *http.Request, secret []byte) ([]byte, error){ +func ValidatePayload(r *http.Request, secret []byte) ([]byte, error){ payload, err := io.ReadAll(r.Body) if err != nil { return nil, fmt.Errorf("error reading request body: %v", err) @@ -143,3 +145,12 @@ func validatePayload(r *http.Request, secret []byte) ([]byte, error){ } +func FixRepoNames(c *GitlabClientImpl) error{ + var formattedRepos []string + for _, repo := range strings.Split(c.cfg.GitProviderConfig.RepoList, ",") { + userRepo := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName ,repo) + formattedRepos = append(formattedRepos, userRepo) + } + c.cfg.GitProviderConfig.RepoList = strings.Join(formattedRepos, ",") + return nil +} \ No newline at end of file diff --git a/pkg/git_provider/gitlab_utils_test.go b/pkg/git_provider/gitlab_utils_test.go index 9a0c94f..88882df 100644 --- a/pkg/git_provider/gitlab_utils_test.go +++ b/pkg/git_provider/gitlab_utils_test.go @@ -13,15 +13,6 @@ import ( "golang.org/x/net/context" ) -// import ( -// "fmt" -// "net/http" -// "testing" - -// assertion "github.com/stretchr/testify/assert" -// "golang.org/x/net/context" -// ) - func mockHTTPResponse(t *testing.T, w io.Writer, response interface{}) { json.NewEncoder(w).Encode(response) } @@ -90,16 +81,21 @@ func TestIsGroupWebhookEnabled(t *testing.T){ client: client, cfg: &conf.GlobalConfig{ GitProviderConfig: conf.GitProviderConfig{ - OrgLevelWebhook: false, + OrgLevelWebhook: true, OrgName: "group1", - RepoList: "test-repo1", + WebhookURL: "testing-url", }, }, } - mux.HandleFunc("api/v4/groups/group1/hooks", func(w http.ResponseWriter, r *http.Request) { + + hook := []gitlab.GroupHook{{ + ID: 1234, + URL: c.cfg.GitProviderConfig.WebhookURL, + },} + + mux.HandleFunc(fmt.Sprintf("/api/v4/groups/%s/hooks", c.cfg.GitProviderConfig.OrgName), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) - fmt.Println("ding ding") - // mockHTTPResponse(t, w, gitlab.User{ID:1234}) + mockHTTPResponse(t, w, hook) }) // // Execute @@ -109,7 +105,47 @@ func TestIsGroupWebhookEnabled(t *testing.T){ // Assert // assert := assertion.New(t) - assert.Equal(isEnabled, false) - assert.NotEqual(string(groupHook.GroupID), "group1") + assert.Equal(isEnabled, true) + assert.Equal(groupHook.URL, c.cfg.GitProviderConfig.WebhookURL) +} + +func TestIsProjectWebhookEnabled(t *testing.T){ + // + // Prepare + // + mux, client := setupGitlab(t) + project := "test-repo1" + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "group1", + WebhookURL: "testing-url", + RepoList: project, + }, + }, + } + + hook := []gitlab.ProjectHook{{ + ID: 1234, + URL: c.cfg.GitProviderConfig.WebhookURL, + },} + + hooksUrl := fmt.Sprintf("/api/v4/projects/%s/%s/hooks",c.cfg.GitProviderConfig.OrgName, project) + mux.HandleFunc(hooksUrl, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + mockHTTPResponse(t, w, hook) + }) + // + // Execute + // + projectHook, isEnabled := IsProjectWebhookEnabled(&c, "test-repo1") + // + // Assert + // + assert := assertion.New(t) + assert.Equal(isEnabled, true) + assert.Equal(projectHook.URL, c.cfg.GitProviderConfig.WebhookURL) } From 38b04cbba15213d4e7e0648c4a787d9207d7640c Mon Sep 17 00:00:00 2001 From: omri assa Date: Sat, 29 Jun 2024 18:19:34 +0300 Subject: [PATCH 14/20] fix: started gitlab tests --- pkg/git_provider/gitlab.go | 3 +- pkg/git_provider/gitlab_test.go | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 pkg/git_provider/gitlab_test.go diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 6dae546..9e4d259 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -58,7 +58,8 @@ func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch s Ref: &branch, Path: &path,} - dirFiles, resp, err := c.client.Repositories.ListTree(repo, opt) + projectName := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName ,repo) + dirFiles, resp, err := c.client.Repositories.ListTree(projectName, opt) if err != nil { diff --git a/pkg/git_provider/gitlab_test.go b/pkg/git_provider/gitlab_test.go new file mode 100644 index 0000000..f65eb53 --- /dev/null +++ b/pkg/git_provider/gitlab_test.go @@ -0,0 +1,72 @@ +package git_provider + +import ( + "fmt" + "net/http" + "testing" + + "github.com/quickube/piper/pkg/conf" + assertion "github.com/stretchr/testify/assert" + "github.com/xanzy/go-gitlab" + "golang.org/x/net/context" +) + +func TestGitlabListFiles(t *testing.T) { + // Prepare + mux, client := setupGitlab(t) + + repoContent := &gitlab.TreeNode{ + Type: "file", + Name: "exit.yaml", + Path: ".workflows/exit.yaml", + } + + repoContent2 := &gitlab.TreeNode{ + Type: "file", + Name: "main.yaml", + Path: ".workflows/main.yaml", + } + + treeNodes := []gitlab.TreeNode{*repoContent, *repoContent2} + expectedRef := "branch1" + project := "project1" + + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{ + OrgLevelWebhook: true, + OrgName: "group1", + RepoList: project, + }, + }, + } + projectUrl := fmt.Sprintf("/api/v4/projects/%s/%s/repository/tree",c.cfg.GitProviderConfig.OrgName, project) + mux.HandleFunc(projectUrl, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + ref := r.URL.Query().Get("ref") + + // Check if the ref value matches the expected value + if ref != expectedRef { + http.Error(w, "Invalid ref value", http.StatusBadRequest) + return + } + mockHTTPResponse(t, w, treeNodes) + }) + + + ctx := context.Background() + + // Execute + actualContent, err := c.ListFiles(&ctx, project, expectedRef, ".workflows") + + var expectedFilesNames []string + for _, file := range treeNodes{ + expectedFilesNames = append(expectedFilesNames, file.Name) + } + + // Assert + assert := assertion.New(t) + assert.NotNil(t, err) + assert.Equal(expectedFilesNames, actualContent) +} From 2eb86788cc6535123991a4a1314dabb2b909fafa Mon Sep 17 00:00:00 2001 From: omri assa Date: Fri, 2 Aug 2024 03:30:12 +0300 Subject: [PATCH 15/20] fix: added some more tests and fixing bugs --- pkg/git_provider/gitlab.go | 16 +- pkg/git_provider/gitlab_test.go | 353 ++++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+), 8 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 9e4d259..a841911 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -109,7 +109,7 @@ func (c *GitlabClientImpl) GetFiles(ctx *context.Context, repo string, branch st func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*HookWithStatus, error) { if c.cfg.OrgLevelWebhook && repo != nil { - return nil, fmt.Errorf("trying to set repo scope. repo: %s", *repo) + return nil, fmt.Errorf("trying to set project scope. project: %s", *repo) } var gitlabHook gitlab.Hook @@ -127,6 +127,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook } gitlabHook, resp, err := c.client.Groups.AddGroupHook(c.cfg.GitProviderConfig.OrgName, &groupHookOptions) + if err != nil { return nil, err } @@ -171,8 +172,8 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook ReleasesEvents: gitlab.Ptr(true), TagPushEvents: gitlab.Ptr(true), } - - gitlabHook, resp, err := c.client.Projects.AddProjectHook(repo, &addProjectHookOpts) + + gitlabHook, resp, err := c.client.Projects.AddProjectHook(*repo, &addProjectHookOpts) if err != nil { return nil, err } @@ -181,7 +182,6 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook } log.Printf("created webhook for %s: %s\n", *repo, gitlabHook.URL) } else { - editProjectHookOpts := gitlab.EditProjectHookOptions{ URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), @@ -190,7 +190,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook ReleasesEvents: gitlab.Ptr(true), TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Projects.EditProjectHook(repo, respHook.ID, &editProjectHookOpts) + gitlabHook, resp, err := c.client.Projects.EditProjectHook(*repo, respHook.ID, &editProjectHookOpts) if err != nil { return nil, err } @@ -285,7 +285,7 @@ func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Req } } - if c.cfg.EnforceOrgBelonging && (webhookPayload.OwnerID == 0 || webhookPayload.OwnerID != c.cfg.OrgID) { + if (webhookPayload.OwnerID == 0) || (webhookPayload.OwnerID != c.cfg.OrgID) { return nil, fmt.Errorf("webhook send from non organizational member") } return webhookPayload, nil @@ -319,7 +319,7 @@ func (c *GitlabClientImpl) SetStatus(ctx *context.Context, repo *string, commit func (c *GitlabClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) error { if c.cfg.OrgLevelWebhook && hook.RepoName != nil { - return fmt.Errorf("trying to ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) + return fmt.Errorf("trying o ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) } if hook.RepoName == nil { _,resp, err := c.client.Groups.GetGroupHook(c.cfg.OrgName,int(hook.HookID), nil) @@ -331,7 +331,7 @@ func (c *GitlabClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) return fmt.Errorf("unable to find organization webhook for hookID: %d", hook.HookID) } } else { - _,resp, err := c.client.Projects.GetProjectHook(hook.RepoName, int(hook.HookID), nil) + _ , resp, err := c.client.Projects.GetProjectHook(*hook.RepoName, int(hook.HookID), nil) if err != nil { return err } diff --git a/pkg/git_provider/gitlab_test.go b/pkg/git_provider/gitlab_test.go index f65eb53..3293640 100644 --- a/pkg/git_provider/gitlab_test.go +++ b/pkg/git_provider/gitlab_test.go @@ -1,11 +1,13 @@ package git_provider import ( + "errors" "fmt" "net/http" "testing" "github.com/quickube/piper/pkg/conf" + "github.com/quickube/piper/pkg/utils" assertion "github.com/stretchr/testify/assert" "github.com/xanzy/go-gitlab" "golang.org/x/net/context" @@ -70,3 +72,354 @@ func TestGitlabListFiles(t *testing.T) { assert.NotNil(t, err) assert.Equal(expectedFilesNames, actualContent) } + +func TestGitlabGetFile(t *testing.T) { + // Prepare + mux, client := setupGitlab(t) + + + expectedFile := gitlab.File{ + Content: "file", + FileName: "file.yaml", + CommitID: "1", + FilePath: ".workflows/file.yaml", + } + + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{ + OrgLevelWebhook: true, + OrgName: "group1", + RepoList: "project1", + }, + }, + } + + mux.HandleFunc("/api/v4/projects/project1/repository/files/.workflows", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + ref := r.URL.Query().Get("ref") + // Check if the ref value matches the expected value + if ref != "branch1" { + http.Error(w, "Invalid ref value", http.StatusBadRequest) + return + } + + mockHTTPResponse(t, w, expectedFile) + }) + + + ctx := context.Background() + + // Execute + actualFile, err := c.GetFile(&ctx, "project1", "branch1", ".workflows") + + // Assert + assert := assertion.New(t) + assert.NotNil(t, err) + + assert.Equal(*actualFile.Path, expectedFile.FilePath) + assert.Equal(*actualFile.Content, expectedFile.Content) +} + +func TestGitlabPingHook(t *testing.T) { + // Prepare + ctx := context.Background() + assert := assertion.New(t) + mux, client := setupGitlab(t) + + hookUrl := "https://url" + + // Test-repo2 existing webhook + mux.HandleFunc("/api/v4/projects/test-repo1/hooks/234", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + mockHTTPResponse(t, w, nil) + }) + + mux.HandleFunc("/api/v4/groups/test/hooks/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + mockHTTPResponse(t, w, nil) + + }) + + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{}, + }, + } + + // Define test cases + tests := []struct { + name string + repo *string + hook *HookWithStatus + config *conf.GitProviderConfig + }{ + { + name: "Ping repo webhook", + hook: &HookWithStatus{ + HookID: 234, + HealthStatus: true, + RepoName: utils.SPtr("test-repo1"), + }, + config: &conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "test", + WebhookURL: hookUrl, + }, + }, + { + name: "Ping org webhook", + hook: &HookWithStatus{ + HookID: 123, + HealthStatus: true, + RepoName: nil, + }, + config: &conf.GitProviderConfig{ + OrgLevelWebhook: true, + OrgName: "test", + WebhookURL: hookUrl, + }, + }, + } + // Run test cases + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + c.cfg.GitProviderConfig = *test.config + // Call the function being tested + err := c.PingHook(&ctx, test.hook) + + assert.Nil(err) + + + }) + } +} + +func TestGitlabSetStatus(t *testing.T) { + // Prepare + ctx := context.Background() + assert := assertion.New(t) + mux, client := setupGitlab(t) + + mux.HandleFunc("/api/v4/projects/test-repo1/statuses/test-commit", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + + w.WriteHeader(http.StatusCreated) + jsonBytes := []byte(`{"status": "ok"}`) + _, _ = fmt.Fprint(w, string(jsonBytes)) + }) + + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "test", + RepoList: "test-repo1", + }, + }, + } + + // Define test cases + tests := []struct { + name string + repo *string + commit *string + linkURL *string + status *string + message *string + wantedError error + }{ + { + name: "Notify success", + repo: utils.SPtr("test-repo1"), + commit: utils.SPtr("test-commit"), + linkURL: utils.SPtr("https://argo"), + status: utils.SPtr("success"), + message: utils.SPtr(""), + wantedError: nil, + }, + { + name: "Notify pending", + repo: utils.SPtr("test-repo1"), + commit: utils.SPtr("test-commit"), + linkURL: utils.SPtr("https://argo"), + status: utils.SPtr("pending"), + message: utils.SPtr(""), + wantedError: nil, + }, + { + name: "Notify error", + repo: utils.SPtr("test-repo1"), + commit: utils.SPtr("test-commit"), + linkURL: utils.SPtr("https://argo"), + status: utils.SPtr("error"), + message: utils.SPtr("some message"), + wantedError: nil, + }, + { + name: "Notify failure", + repo: utils.SPtr("test-repo1"), + commit: utils.SPtr("test-commit"), + linkURL: utils.SPtr("https://argo"), + status: utils.SPtr("failure"), + message: utils.SPtr(""), + wantedError: nil, + }, + { + name: "Non managed repo", + repo: utils.SPtr("non-existing-repo"), + commit: utils.SPtr("test-commit"), + linkURL: utils.SPtr("https://argo"), + status: utils.SPtr("error"), + message: utils.SPtr(""), + wantedError: errors.New("some error"), + }, + { + name: "Non existing commit", + repo: utils.SPtr("test-repo1"), + commit: utils.SPtr("not-exists"), + linkURL: utils.SPtr("https://argo"), + status: utils.SPtr("error"), + message: utils.SPtr(""), + wantedError: errors.New("some error"), + }, + { + name: "Wrong URL", + repo: utils.SPtr("test-repo1"), + commit: utils.SPtr("test-commit"), + linkURL: utils.SPtr("argo"), + status: utils.SPtr("error"), + message: utils.SPtr(""), + wantedError: errors.New("some error"), + }, + } + // Run test cases + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + + // Call the function being tested + err := c.SetStatus(&ctx, test.repo, test.commit, test.linkURL, test.status, test.message) + + // Use assert to check the equality of the error + if test.wantedError != nil { + assert.Error(err) + assert.NotNil(err) + } else { + assert.NoError(err) + assert.Nil(err) + } + }) + } +} + + + +func TestGitlabSetWebhook(t *testing.T) { + // Prepare + ctx := context.Background() + assert := assertion.New(t) + mux, client := setupGitlab(t) + + hookUrl := "https://url" + + // new project Webhook + mux.HandleFunc("/api/v4/projects/test/test-repo1/hooks", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + mockHTTPResponse(t,w,[]*gitlab.ProjectHook{}) + } + }) + mux.HandleFunc("/api/v4/projects/test-repo1/hooks", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + w.WriteHeader(http.StatusCreated) + mockHTTPResponse(t,w, &gitlab.ProjectHook{ID:123,URL: hookUrl}) + } + }) + // existing project webhook + mux.HandleFunc("/api/v4/projects/test/test-repo2/hooks", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + mockHTTPResponse(t,w,[]*gitlab.ProjectHook{{ID:123,URL: hookUrl}}) + } + }) + mux.HandleFunc("/api/v4/projects/test-repo2/hooks/123", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "PUT" { + w.WriteHeader(http.StatusOK) + mockHTTPResponse(t,w, &gitlab.ProjectHook{ID:123,URL: hookUrl}) + } + }) + + + + // #TODO: + // # new group webhook + // # exisitng group webhook + // # error if group and project + + + + + + + + + + + c := GitlabClientImpl{ + client: client, + cfg: &conf.GlobalConfig{ + GitProviderConfig: conf.GitProviderConfig{}, + }, + } + + // Define test cases + tests := []struct { + name string + repo *string + config *conf.GitProviderConfig + wantedError error + }{ + { + name: "New project webhook", + repo: utils.SPtr("test-repo1"), + config: &conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "test", + RepoList: "test-repo1", + WebhookURL: hookUrl, + }, + wantedError: nil, + }, + { + name: "Existing project webhook", + repo: utils.SPtr("test-repo2"), + config: &conf.GitProviderConfig{ + OrgLevelWebhook: false, + OrgName: "test", + RepoList: "test-repo2", + WebhookURL: hookUrl, + }, + wantedError: nil, + }, + + } + // Run test cases + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + c.cfg.GitProviderConfig = *test.config + // Call the function being tested + _, err := c.SetWebhook(&ctx, test.repo) + + // Use assert to check the equality of the error + if test.wantedError != nil { + assert.NotNil(err) + } else { + assert.Nil(err) + //assert.Equal(hookUrl, hook.Config["url"]) + } + }) + } + +} \ No newline at end of file From 807f9deea63940a0de94aa8f50b75afe38263bc5 Mon Sep 17 00:00:00 2001 From: omri assa Date: Mon, 5 Aug 2024 00:17:49 +0300 Subject: [PATCH 16/20] fix: finished tests now runnning qa --- pkg/git_provider/gitlab.go | 7 +-- pkg/git_provider/gitlab_test.go | 80 ++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index a841911..50a0b7a 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -127,7 +127,6 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook } gitlabHook, resp, err := c.client.Groups.AddGroupHook(c.cfg.GitProviderConfig.OrgName, &groupHookOptions) - if err != nil { return nil, err } @@ -144,11 +143,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook ReleasesEvents: gitlab.Ptr(true), TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Groups.EditGroupHook( - c.cfg.GitProviderConfig.OrgName, - respHook.ID, - &editedGroupHookOpt, - ) + gitlabHook, resp, err := c.client.Groups.EditGroupHook(c.cfg.GitProviderConfig.OrgName,respHook.ID,&editedGroupHookOpt,) if err != nil { return nil, err } diff --git a/pkg/git_provider/gitlab_test.go b/pkg/git_provider/gitlab_test.go index 3293640..900b284 100644 --- a/pkg/git_provider/gitlab_test.go +++ b/pkg/git_provider/gitlab_test.go @@ -316,8 +316,6 @@ func TestGitlabSetStatus(t *testing.T) { } } - - func TestGitlabSetWebhook(t *testing.T) { // Prepare ctx := context.Background() @@ -326,16 +324,38 @@ func TestGitlabSetWebhook(t *testing.T) { hookUrl := "https://url" + // new group webhook + mux.HandleFunc("/api/v4/groups/groupA/hooks", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + mockHTTPResponse(t,w,[]*gitlab.GroupHook{}) + } else if r.Method == "POST"{ + w.WriteHeader(http.StatusCreated) + mockHTTPResponse(t,w,gitlab.GroupHook{ID:123,URL: hookUrl}) + } + }) + // existing group Webhook + mux.HandleFunc("/api/v4/groups/groupB/hooks", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + mockHTTPResponse(t,w,[]*gitlab.GroupHook{{ID:123,URL: hookUrl}}) + } + }) + mux.HandleFunc("/api/v4/groups/groupB/hooks/123", func(w http.ResponseWriter, r *http.Request) { + if r.Method == "PUT"{ + w.WriteHeader(http.StatusOK) + mockHTTPResponse(t,w,gitlab.GroupHook{ID:123,URL: hookUrl}) + } + }) + // new project Webhook mux.HandleFunc("/api/v4/projects/test/test-repo1/hooks", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { - mockHTTPResponse(t,w,[]*gitlab.ProjectHook{}) + mockHTTPResponse(t,w,[]*gitlab.ProjectHook{{}}) } }) mux.HandleFunc("/api/v4/projects/test-repo1/hooks", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { w.WriteHeader(http.StatusCreated) - mockHTTPResponse(t,w, &gitlab.ProjectHook{ID:123,URL: hookUrl}) + mockHTTPResponse(t,w, gitlab.ProjectHook{ID:123,URL: hookUrl}) } }) // existing project webhook @@ -347,26 +367,12 @@ func TestGitlabSetWebhook(t *testing.T) { mux.HandleFunc("/api/v4/projects/test-repo2/hooks/123", func(w http.ResponseWriter, r *http.Request) { if r.Method == "PUT" { w.WriteHeader(http.StatusOK) - mockHTTPResponse(t,w, &gitlab.ProjectHook{ID:123,URL: hookUrl}) + mockHTTPResponse(t,w, gitlab.ProjectHook{ID:123,URL: hookUrl}) } }) - // #TODO: - // # new group webhook - // # exisitng group webhook - // # error if group and project - - - - - - - - - - c := GitlabClientImpl{ client: client, cfg: &conf.GlobalConfig{ @@ -381,6 +387,39 @@ func TestGitlabSetWebhook(t *testing.T) { config *conf.GitProviderConfig wantedError error }{ + { + name: "Repo and orgWebHook enabled", + repo: utils.SPtr("repo"), + config: &conf.GitProviderConfig{ + OrgLevelWebhook: true, + OrgName: "test", + RepoList: "test-repo1", + WebhookURL: hookUrl, + }, + wantedError: errors.New("error"), + }, + { + name: "New group webhook", + repo: nil, + config: &conf.GitProviderConfig{ + OrgLevelWebhook: true, + OrgName: "groupA", + RepoList: "test-repo1", + WebhookURL: hookUrl, + }, + wantedError: nil, + }, + { + name: "Existing group webhook", + repo: nil, + config: &conf.GitProviderConfig{ + OrgLevelWebhook: true, + OrgName: "groupB", + RepoList: "test-repo1", + WebhookURL: hookUrl, + }, + wantedError: nil, + }, { name: "New project webhook", repo: utils.SPtr("test-repo1"), @@ -421,5 +460,4 @@ func TestGitlabSetWebhook(t *testing.T) { } }) } - -} \ No newline at end of file +} From 81b4dae8b8ee7cef63e598b73eb3e487ab3709a1 Mon Sep 17 00:00:00 2001 From: omri assa Date: Mon, 5 Aug 2024 00:24:48 +0300 Subject: [PATCH 17/20] fix: added some docs --- docs/configuration/environment_variables.md | 49 +++++++++++---------- docs/getting_started/installation.md | 25 ++++++----- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/docs/configuration/environment_variables.md b/docs/configuration/environment_variables.md index e96f0b8..6056a55 100644 --- a/docs/configuration/environment_variables.md +++ b/docs/configuration/environment_variables.md @@ -5,56 +5,59 @@ The helm chart populate them using [values.yaml](https://github.com/quickube/pip ### Git -* GIT_PROVIDER - The git provider that Piper will use, possible variables: GitHub (will support bitbucket and gitlab) +- GIT_PROVIDER + The git provider that Piper will use, possible variables: GitHub | Gitlab (will support bitbucket) -* GIT_TOKEN +- GIT_TOKEN The git token that will be used. -* GIT_ORG_NAME +- GIT_URL + the git url that will be used, mostly relevant when running gitlab on-prem + +- GIT_ORG_NAME The organization name. -* GIT_ORG_LEVEL_WEBHOOK +- GIT_ORG_LEVEL_WEBHOOK Boolean variable, whether to config webhook in organization level. default `false` -* GIT_WEBHOOK_REPO_LIST +- GIT_WEBHOOK_REPO_LIST List of repositories to configure webhooks to. -* GIT_WEBHOOK_URL +- GIT_WEBHOOK_URL URL of piper ingress, to configure webhooks. -* GIT_WEBHOOK_AUTO_CLEANUP - Will cleanup all webhook that were created with piper. - Notice that there will be a race conditions between pod that being terminated and the new one. +- GIT_WEBHOOK_AUTO_CLEANUP + Will cleanup all webhook that were created with piper. + Notice that there will be a race conditions between pod that being terminated and the new one. -* GIT_ENFORCE_ORG_BELONGING +- GIT_ENFORCE_ORG_BELONGING Boolean variable, whether to enforce organizational belonging of git event creator. default `false` -* GIT_FULL_HEALTH_CHECK +- GIT_FULL_HEALTH_CHECK Enables full health check of webhook. Full health check contains expecting and validating ping event from a webhook. Doesn't work for bitbucket, because the API call doesn't exists. - ### Argo Workflows Server -* ARGO_WORKFLOWS_TOKEN + +- ARGO_WORKFLOWS_TOKEN The token of Argo Workflows server. -* ARGO_WORKFLOWS_ADDRESS +- ARGO_WORKFLOWS_ADDRESS The address of Argo Workflows Server. - -* ARGO_WORKFLOWS_CREATE_CRD +- ARGO_WORKFLOWS_CREATE_CRD Whether to directly send Workflows instruction or create a CRD in the Cluster. -* ARGO_WORKFLOWS_NAMESPACE +- ARGO_WORKFLOWS_NAMESPACE The namespace of Workflows creation for Argo Workflows. -* KUBE_CONFIG +- KUBE_CONFIG Used to configure Argo Workflows client with local kube configurations. ### Rookout -* ROOKOUT_TOKEN + +- ROOKOUT_TOKEN The token used to configure Rookout agent. If not provided, will not start the agent. -* ROOKOUT_LABELS +- ROOKOUT_LABELS The labels to label instances at Rookout, default to "service:piper" -* ROOKOUT_REMOTE_ORIGIN - The repo URL for source code fetching, default:"https://github.com/quickube/piper.git". \ No newline at end of file +- ROOKOUT_REMOTE_ORIGIN + The repo URL for source code fetching, default:"https://github.com/quickube/piper.git". diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index 5ab16c0..2eab676 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -1,16 +1,18 @@ ## Instalation -Piper should be deployed in the cluster with Argo Workflows. -Piper will create a CRD that Argo Workflows will pick, so install or configure Piper to create those CRDs in the right namespace. +Piper should be deployed in the cluster with Argo Workflows. +Piper will create a CRD that Argo Workflows will pick, so install or configure Piper to create those CRDs in the right namespace. Please check out [values.yaml](https://github.com/quickube/piper/tree/main/helm-chart/values.yaml) file of the helm chart configurations. To add piper helm repo run: + ```bash helm repo add piper https://piper.quickube.com ``` After configuring Piper [values.yaml](https://github.com/quickube/piper/tree/main/helm-chart/values.yaml), run the following command for installation: + ```bash helm upgrade --install piper piper/piper \ -f YOUR_VALUES_FILE.yaml @@ -22,7 +24,7 @@ helm upgrade --install piper piper/piper \ ### Ingress -Piper should listen to webhooks from your git provider. +Piper should listen to webhooks from your git provider. Expose it using ingress or service, then provide the address to `piper.webhook.url` as followed: `https://PIPER_EXPOESED_URL/webhook` @@ -32,9 +34,9 @@ Checkout [values.yaml](https://github.com/quickube/piper/tree/main/helm-chart/va Piper will use git for fetching `.workflows` folder and receiving events using webhooks. -To pick which git provider you are using provide `gitProvider.name` configuration in helm chart (Now only supports GitHub and Bitbucket). +To pick which git provider you are using provide `gitProvider.name` configuration in helm chart (supports GitHub, Bitbucket and Gitlab). -Also configure you organization (Github) or workspace (Bitbucket) name using `gitProvider.organization.name` in helm chart. +Also configure you organization (Github), workspace (Bitbucket) or group (Gitlab) name using `gitProvider.organization.name` in helm chart. #### Git Token Permissions @@ -44,11 +46,12 @@ For Bitbucket configure `Repositories:read`, `Webhooks:read and write` and `Pull #### Token -The git token should be passed as secret in the helm chart at `gitProvider.token`. +The git token should be passed as secret in the helm chart at `gitProvider.token`. Can be passed as parameter in helm install command using `--set piper.gitProvider.token=YOUR_GIT_TOKEN` Alternatively, you can consume already existing secret and fill up `piper.gipProvider.existingSecret`. -The key should be name `token`. Can be created using +The key should be name `token`. Can be created using + ```bash kubectl create secret generic piper-git-token --from-literal=token=YOUR_GIT_OKEN ``` @@ -61,13 +64,13 @@ Configure `piper.webhook.url` the address of piper that exposed with ingress wit For organization level configure: `gitProvider.webhook.orgLevel` to `true`. -For granular repo webhook provide list of repos at: `gitProvider.webhook.repoList`. +For granular repo webhook provide list of repos at: `gitProvider.webhook.repoList`. -Piper implements graceful shutdown, it will delete all the webhooks when terminated. +Piper implements graceful shutdown, it will delete all the webhooks when terminated. #### Status check -Piper will handle status checks for you. +Piper will handle status checks for you. It will notify the GitProvider for the status of Workflow for specific commit that triggered Piper. For linking provide valid URL of your Argo Workflows server address at: `argoWorkflows.server.address` @@ -81,4 +84,4 @@ To lint the workflow before submitting it, please configure the internal address #### Skip CRD Creation (On development) -Piper can communicate directly to Argo Workflow using ARGO_WORKFLOWS_CREATE_CRD environment variable, if you want to skip the creation of CRD change `argoWorkflows.crdCreation` to `false`. \ No newline at end of file +Piper can communicate directly to Argo Workflow using ARGO_WORKFLOWS_CREATE_CRD environment variable, if you want to skip the creation of CRD change `argoWorkflows.crdCreation` to `false`. From df72152d49d4b675f55ccbc586329cb204b5c018 Mon Sep 17 00:00:00 2001 From: omri assa Date: Mon, 5 Aug 2024 00:36:39 +0300 Subject: [PATCH 18/20] fix: added some validations and docs --- docs/configuration/environment_variables.md | 2 +- pkg/conf/git_provider.go | 2 +- pkg/git_provider/gitlab.go | 2 ++ pkg/git_provider/gitlab_utils.go | 8 ++++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/configuration/environment_variables.md b/docs/configuration/environment_variables.md index 6056a55..eb4c0d3 100644 --- a/docs/configuration/environment_variables.md +++ b/docs/configuration/environment_variables.md @@ -12,7 +12,7 @@ The helm chart populate them using [values.yaml](https://github.com/quickube/pip The git token that will be used. - GIT_URL - the git url that will be used, mostly relevant when running gitlab on-prem + the git url that will be used, only relevant when running gitlab self hosted - GIT_ORG_NAME The organization name. diff --git a/pkg/conf/git_provider.go b/pkg/conf/git_provider.go index 16bb76c..5bf3d9a 100644 --- a/pkg/conf/git_provider.go +++ b/pkg/conf/git_provider.go @@ -9,7 +9,7 @@ import ( type GitProviderConfig struct { Provider string `envconfig:"GIT_PROVIDER" required:"true"` Token string `envconfig:"GIT_TOKEN" required:"true"` - Url string `envconfig:"GIT_URL" required:"true"` + Url string `envconfig:"GIT_URL" required:"false"` OrgName string `envconfig:"GIT_ORG_NAME" required:"true"` OrgLevelWebhook bool `envconfig:"GIT_ORG_LEVEL_WEBHOOK" default:"false" required:"false"` RepoList string `envconfig:"GIT_WEBHOOK_REPO_LIST" required:"false"` diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 50a0b7a..f67b259 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -41,6 +41,8 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { return nil, fmt.Errorf("failed to get organization data %s", resp.Status) } + cfg = EnsureGitlabURL(cfg) + c := &GitlabClientImpl{ client: client, cfg: cfg, diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index bdcf9da..7b9e433 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -153,4 +153,12 @@ func FixRepoNames(c *GitlabClientImpl) error{ } c.cfg.GitProviderConfig.RepoList = strings.Join(formattedRepos, ",") return nil +} + +func EnsureGitlabURL(c *conf.GlobalConfig) *conf.GlobalConfig{ + saasGitlabUrl := "https://gitlab.com" + if c.Url == "" { + c.Url = saasGitlabUrl + } + return c } \ No newline at end of file From a280f991d52812c1e7635e4a8bf1cd92c7d649e3 Mon Sep 17 00:00:00 2001 From: goshado Date: Sun, 10 Nov 2024 20:53:43 +0200 Subject: [PATCH 19/20] fix: context and git provider factory --- pkg/git_provider/gitlab.go | 94 ++++++++++++++++---------------- pkg/git_provider/gitlab_utils.go | 58 ++++++++------------ pkg/git_provider/main.go | 6 ++ 3 files changed, 76 insertions(+), 82 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index f67b259..763428c 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -20,9 +20,14 @@ type GitlabClientImpl struct { } func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { + var options []gitlab.ClientOptionFunc ctx := context.Background() - client, err := gitlab.NewClient(cfg.GitProviderConfig.Token, gitlab.WithBaseURL(cfg.GitProviderConfig.Url)) + if cfg.GitProviderConfig.Url != "" { + options = append(options, gitlab.WithBaseURL(cfg.GitProviderConfig.Url)) + } + + client, err := gitlab.NewClient(cfg.GitProviderConfig.Token, options...) if err != nil { return nil, err } @@ -41,28 +46,23 @@ func NewGitlabClient(cfg *conf.GlobalConfig) (Client, error) { return nil, fmt.Errorf("failed to get organization data %s", resp.Status) } - cfg = EnsureGitlabURL(cfg) + cfg.GitProviderConfig.OrgID = int64(group.ID) + log.Printf("Org ID is: %d\n", cfg.OrgID) - c := &GitlabClientImpl{ + return &GitlabClientImpl{ client: client, cfg: cfg, - } - - c.cfg.GitProviderConfig.OrgID = int64(group.ID) - log.Printf("Org ID is: %d\n", cfg.OrgID) - - return c, err + }, err } func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch string, path string) ([]string, error) { var files []string opt := &gitlab.ListTreeOptions{ - Ref: &branch, - Path: &path,} - - projectName := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName ,repo) - dirFiles, resp, err := c.client.Repositories.ListTree(projectName, opt) + Ref: &branch, + Path: &path} + projectName := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName, repo) + dirFiles, resp, err := c.client.Repositories.ListTree(projectName, opt, gitlab.WithContext(*ctx)) if err != nil { return nil, err @@ -79,8 +79,7 @@ func (c *GitlabClientImpl) ListFiles(ctx *context.Context, repo string, branch s func (c *GitlabClientImpl) GetFile(ctx *context.Context, repo string, branch string, path string) (*CommitFile, error) { var commitFile CommitFile - opt := &gitlab.GetFileOptions{Ref: &branch,} - fileContent, resp, err := c.client.RepositoryFiles.GetFile(repo, path,opt) + fileContent, resp, err := c.client.RepositoryFiles.GetFile(repo, path, &gitlab.GetFileOptions{Ref: &branch}, gitlab.WithContext(*ctx)) if err != nil { return &commitFile, err } @@ -116,16 +115,16 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook var gitlabHook gitlab.Hook if repo == nil { - respHook, ok := IsGroupWebhookEnabled(c) + respHook, ok := IsGroupWebhookEnabled(ctx, c) if !ok { groupHookOptions := gitlab.AddGroupHookOptions{ - URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), - Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), MergeRequestsEvents: gitlab.Ptr(true), - PushEvents: gitlab.Ptr(true), - ReleasesEvents: gitlab.Ptr(true), - TagPushEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), } gitlabHook, resp, err := c.client.Groups.AddGroupHook(c.cfg.GitProviderConfig.OrgName, &groupHookOptions) @@ -138,14 +137,14 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook log.Printf("added webhook for %s name: %s\n", c.cfg.GitProviderConfig.OrgName, gitlabHook.URL) } else { editedGroupHookOpt := gitlab.EditGroupHookOptions{ - URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), - Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), MergeRequestsEvents: gitlab.Ptr(true), - PushEvents: gitlab.Ptr(true), - ReleasesEvents: gitlab.Ptr(true), - TagPushEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Groups.EditGroupHook(c.cfg.GitProviderConfig.OrgName,respHook.ID,&editedGroupHookOpt,) + gitlabHook, resp, err := c.client.Groups.EditGroupHook(c.cfg.GitProviderConfig.OrgName, respHook.ID, &editedGroupHookOpt) if err != nil { return nil, err } @@ -159,17 +158,17 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook log.Printf("edited webhook for %s: %s\n", c.cfg.GitProviderConfig.OrgName, gitlabHook.URL) } } else { - respHook, ok := IsProjectWebhookEnabled(c, *repo) + respHook, ok := IsProjectWebhookEnabled(ctx, c, *repo) if !ok { addProjectHookOpts := gitlab.AddProjectHookOptions{ - URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), - Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), MergeRequestsEvents: gitlab.Ptr(true), - PushEvents: gitlab.Ptr(true), - ReleasesEvents: gitlab.Ptr(true), - TagPushEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), } - + gitlabHook, resp, err := c.client.Projects.AddProjectHook(*repo, &addProjectHookOpts) if err != nil { return nil, err @@ -180,12 +179,12 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook log.Printf("created webhook for %s: %s\n", *repo, gitlabHook.URL) } else { editProjectHookOpts := gitlab.EditProjectHookOptions{ - URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), - Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), + URL: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookURL), + Token: gitlab.Ptr(c.cfg.GitProviderConfig.WebhookSecret), MergeRequestsEvents: gitlab.Ptr(true), - PushEvents: gitlab.Ptr(true), - ReleasesEvents: gitlab.Ptr(true), - TagPushEvents: gitlab.Ptr(true), + PushEvents: gitlab.Ptr(true), + ReleasesEvents: gitlab.Ptr(true), + TagPushEvents: gitlab.Ptr(true), } gitlabHook, resp, err := c.client.Projects.EditProjectHook(*repo, respHook.ID, &editProjectHookOpts) if err != nil { @@ -206,7 +205,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook func (c *GitlabClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStatus) error { if hook.RepoName == nil { - resp, err := c.client.Groups.DeleteGroupHook( c.cfg.GitProviderConfig.OrgName, int(hook.HookID)) + resp, err := c.client.Groups.DeleteGroupHook(c.cfg.GitProviderConfig.OrgName, int(hook.HookID), gitlab.WithContext(*ctx)) if err != nil { return err } @@ -216,7 +215,7 @@ func (c *GitlabClientImpl) UnsetWebhook(ctx *context.Context, hook *HookWithStat } log.Printf("removed group webhook, hookID :%d\n", hook.HookID) } else { - resp, err := c.client.Projects.DeleteProjectHook(*hook.RepoName, int(hook.HookID)) + resp, err := c.client.Projects.DeleteProjectHook(*hook.RepoName, int(hook.HookID), gitlab.WithContext(*ctx)) if err != nil { return fmt.Errorf("failed to delete project level webhhok for %s, API call returned %d. %s", *hook.RepoName, resp.StatusCode, err) @@ -242,7 +241,6 @@ func (c *GitlabClientImpl) HandlePayload(ctx *context.Context, request *http.Req return nil, err } - switch e := event.(type) { case gitlab.PushEvent: webhookPayload = &WebhookPayload{ @@ -295,13 +293,13 @@ func (c *GitlabClientImpl) SetStatus(ctx *context.Context, repo *string, commit repoStatus := &gitlab.SetCommitStatusOptions{ State: gitlab.BuildStateValue(*status), // pending, success, error, or failure. - Ref: commit, + Ref: commit, TargetURL: linkURL, Description: gitlab.Ptr(fmt.Sprintf("Workflow %s %s", *status, *message)), Context: gitlab.Ptr("Piper/ArgoWorkflows"), } - - _, resp, err := c.client.Commits.SetCommitStatus(*repo, *commit, repoStatus) + + _, resp, err := c.client.Commits.SetCommitStatus(*repo, *commit, repoStatus, gitlab.WithContext(*ctx)) if err != nil { return err } @@ -319,7 +317,7 @@ func (c *GitlabClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) return fmt.Errorf("trying o ping repo scope webhook while configured for org level webhook. repo: %s", *hook.RepoName) } if hook.RepoName == nil { - _,resp, err := c.client.Groups.GetGroupHook(c.cfg.OrgName,int(hook.HookID), nil) + _, resp, err := c.client.Groups.GetGroupHook(c.cfg.OrgName, int(hook.HookID), nil, gitlab.WithContext(*ctx)) if err != nil { return err } @@ -328,7 +326,7 @@ func (c *GitlabClientImpl) PingHook(ctx *context.Context, hook *HookWithStatus) return fmt.Errorf("unable to find organization webhook for hookID: %d", hook.HookID) } } else { - _ , resp, err := c.client.Projects.GetProjectHook(*hook.RepoName, int(hook.HookID), nil) + _, resp, err := c.client.Projects.GetProjectHook(*hook.RepoName, int(hook.HookID), nil, gitlab.WithContext(*ctx)) if err != nil { return err } diff --git a/pkg/git_provider/gitlab_utils.go b/pkg/git_provider/gitlab_utils.go index 7b9e433..d1e847e 100644 --- a/pkg/git_provider/gitlab_utils.go +++ b/pkg/git_provider/gitlab_utils.go @@ -40,26 +40,25 @@ func ValidateGitlabPermissions(ctx context.Context, client *gitlab.Client, cfg * } func getGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, error) { - - user, resp,err := client.Users.CurrentUser() - fmt.Println(user.ID) - if err != nil{ + + user, resp, err := client.Users.CurrentUser(gitlab.WithContext(ctx)) + if err != nil { return nil, err - } - if resp.StatusCode == 400 { + } + if resp.StatusCode == 400 { return nil, err - } - a := gitlab.ListPersonalAccessTokensOptions{ - UserID: &user.ID, } - accessTokens, resp,err := client.PersonalAccessTokens.ListPersonalAccessTokens(&a) - fmt.Println(accessTokens) - if err != nil{ + a := gitlab.ListPersonalAccessTokensOptions{ + UserID: &user.ID, + } + accessTokens, resp, err := client.PersonalAccessTokens.ListPersonalAccessTokens(&a) + fmt.Println(accessTokens) + if err != nil { return nil, err - } - if resp.StatusCode == 400 { + } + if resp.StatusCode == 400 { return nil, err - } + } scopes := accessTokens[0].Scopes fmt.Println("Gitlab Token Scopes are:", scopes) @@ -67,9 +66,9 @@ func getGitlabScopes(ctx context.Context, client *gitlab.Client) ([]string, erro return scopes, nil } -func IsGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { +func IsGroupWebhookEnabled(ctx *context.Context, c *GitlabClientImpl) (*gitlab.GroupHook, bool) { emptyHook := gitlab.GroupHook{} - hooks, resp, err := c.client.Groups.ListGroupHooks(c.cfg.GitProviderConfig.OrgName, nil) + hooks, resp, err := c.client.Groups.ListGroupHooks(c.cfg.GitProviderConfig.OrgName, nil, gitlab.WithContext(*ctx)) if err != nil { return &emptyHook, false } @@ -87,10 +86,10 @@ func IsGroupWebhookEnabled(c *GitlabClientImpl) (*gitlab.GroupHook, bool) { return &emptyHook, false } -func IsProjectWebhookEnabled(c *GitlabClientImpl, project string) (*gitlab.ProjectHook, bool) { +func IsProjectWebhookEnabled(ctx *context.Context, c *GitlabClientImpl, project string) (*gitlab.ProjectHook, bool) { emptyHook := gitlab.ProjectHook{} - projectFullName := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName ,project) - hooks, resp, err := c.client.Projects.ListProjectHooks(projectFullName, nil) + projectFullName := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName, project) + hooks, resp, err := c.client.Projects.ListProjectHooks(projectFullName, nil, gitlab.WithContext(*ctx)) if err != nil { return &emptyHook, false } @@ -118,7 +117,7 @@ func ExtractLabelsId(labels []*gitlab.EventLabel) []string { return returnLabelsList } -func ValidatePayload(r *http.Request, secret []byte) ([]byte, error){ +func ValidatePayload(r *http.Request, secret []byte) ([]byte, error) { payload, err := io.ReadAll(r.Body) if err != nil { return nil, fmt.Errorf("error reading request body: %v", err) @@ -137,28 +136,19 @@ func ValidatePayload(r *http.Request, secret []byte) ([]byte, error){ } expectedMAC := hex.EncodeToString(h.Sum(nil)) - isEquall := hmac.Equal([]byte(gitlabSignature), []byte(expectedMAC)) - if !isEquall { + isEqual := hmac.Equal([]byte(gitlabSignature), []byte(expectedMAC)) + if !isEqual { return nil, fmt.Errorf("secret not correct") } return payload, nil } - -func FixRepoNames(c *GitlabClientImpl) error{ +func FixRepoNames(c *GitlabClientImpl) error { var formattedRepos []string for _, repo := range strings.Split(c.cfg.GitProviderConfig.RepoList, ",") { - userRepo := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName ,repo) + userRepo := fmt.Sprintf("%s/%s", c.cfg.GitProviderConfig.OrgName, repo) formattedRepos = append(formattedRepos, userRepo) } c.cfg.GitProviderConfig.RepoList = strings.Join(formattedRepos, ",") return nil } - -func EnsureGitlabURL(c *conf.GlobalConfig) *conf.GlobalConfig{ - saasGitlabUrl := "https://gitlab.com" - if c.Url == "" { - c.Url = saasGitlabUrl - } - return c -} \ No newline at end of file diff --git a/pkg/git_provider/main.go b/pkg/git_provider/main.go index 9ebc5fa..6143b9a 100644 --- a/pkg/git_provider/main.go +++ b/pkg/git_provider/main.go @@ -20,6 +20,12 @@ func NewGitProviderClient(cfg *conf.GlobalConfig) (Client, error) { return nil, err } return gitClient, nil + case "gitlab": + gitClient, err := NewGitlabClient(cfg) + if err != nil { + return nil, err + } + return gitClient, nil } return nil, fmt.Errorf("didn't find matching git provider %s", cfg.GitProviderConfig.Provider) From 1d2c72f3aa035211ce85419cdef19fe7bf4c7c48 Mon Sep 17 00:00:00 2001 From: goshado Date: Sun, 10 Nov 2024 21:01:58 +0200 Subject: [PATCH 20/20] fix: add context --- pkg/git_provider/gitlab.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/git_provider/gitlab.go b/pkg/git_provider/gitlab.go index 763428c..53d04e5 100644 --- a/pkg/git_provider/gitlab.go +++ b/pkg/git_provider/gitlab.go @@ -127,7 +127,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Groups.AddGroupHook(c.cfg.GitProviderConfig.OrgName, &groupHookOptions) + gitlabHook, resp, err := c.client.Groups.AddGroupHook(c.cfg.GitProviderConfig.OrgName, &groupHookOptions, gitlab.WithContext(*ctx)) if err != nil { return nil, err } @@ -144,7 +144,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook ReleasesEvents: gitlab.Ptr(true), TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Groups.EditGroupHook(c.cfg.GitProviderConfig.OrgName, respHook.ID, &editedGroupHookOpt) + gitlabHook, resp, err := c.client.Groups.EditGroupHook(c.cfg.GitProviderConfig.OrgName, respHook.ID, &editedGroupHookOpt, gitlab.WithContext(*ctx)) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Projects.AddProjectHook(*repo, &addProjectHookOpts) + gitlabHook, resp, err := c.client.Projects.AddProjectHook(*repo, &addProjectHookOpts, gitlab.WithContext(*ctx)) if err != nil { return nil, err } @@ -186,7 +186,7 @@ func (c *GitlabClientImpl) SetWebhook(ctx *context.Context, repo *string) (*Hook ReleasesEvents: gitlab.Ptr(true), TagPushEvents: gitlab.Ptr(true), } - gitlabHook, resp, err := c.client.Projects.EditProjectHook(*repo, respHook.ID, &editProjectHookOpts) + gitlabHook, resp, err := c.client.Projects.EditProjectHook(*repo, respHook.ID, &editProjectHookOpts, gitlab.WithContext(*ctx)) if err != nil { return nil, err }