Skip to content

Commit

Permalink
gitlab branch protection dynamodb event handlers (#3246)
Browse files Browse the repository at this point in the history
  • Loading branch information
makkalot authored Sep 7, 2021
1 parent f998d72 commit 742c4f4
Show file tree
Hide file tree
Showing 8 changed files with 570 additions and 1 deletion.
4 changes: 4 additions & 0 deletions cla-backend-go/cmd/dynamo_events_lambda/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"encoding/json"
"os"

v2Repositories "github.com/communitybridge/easycla/cla-backend-go/v2/repositories"

"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"

gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
Expand Down Expand Up @@ -85,6 +87,7 @@ func init() {
userRepo := user.NewDynamoRepository(awsSession, stage)
companyRepo := company.NewRepository(awsSession, stage)
projectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
v2Repository := v2Repositories.NewRepository(awsSession, stage)
repositoriesRepo := repositories.NewRepository(awsSession, stage)
gerritRepo := gerrits.NewRepository(awsSession, stage)
projectRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, projectClaGroupRepo)
Expand Down Expand Up @@ -142,6 +145,7 @@ func init() {
eventsRepo,
projectRepo,
gitlabOrganizationRepo,
v2Repository,
projectService,
githubOrganizationsService,
repositoriesService,
Expand Down
146 changes: 146 additions & 0 deletions cla-backend-go/cmd/gitlab/branch_protection/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT

package main

import (
"flag"
"fmt"
"os"

log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/xanzy/go-gitlab"
)

const (
possibleDefaultBranch = "main"
)

var projectID = flag.Int("project", 0, "gitlab project id")

func main() {
flag.Parse()

if *projectID == 0 {
log.Fatalf("gitlab project id is missing")
}

accessToken := os.Getenv("GITLAB_ACCESS_TOKEN")
if accessToken == "" {
log.Fatalf("GITLAB_ACCESS_TOKEN is required")
}

gitlabClient, err := gitlab.NewOAuthClient(accessToken)
if err != nil {
log.Fatalf("creating client failed : %v", err)
}

defaultBranch, err := getDefaultBranch(gitlabClient, *projectID)
if err != nil {
log.Fatalf("fetching the default branch failed : %v", err)
}

log.Println("the default branch found is : ", defaultBranch.Name)
if err := setOrCreateProtection(gitlabClient, *projectID, defaultBranch.Name); err != nil {
log.Fatalf("setting branch protection for : %s failed : %v", defaultBranch.Name, err)
}

log.Println("branch protection set for : ", defaultBranch.Name)
}

func setOrCreateProtection(client *gitlab.Client, projectID int, protectionPattern string) error {
var err error

protectedBranch, resp, err := client.ProtectedBranches.GetProtectedBranch(projectID, protectionPattern)
if err != nil && resp.StatusCode != 404 {
return fmt.Errorf("fetching existing branch failed : %v", err)
}

if protectedBranch != nil {
if isProtectedBranchSet(protectedBranch) {
log.Println("branch protection already set, nothing to do")
return nil
}
//it's an existing one try to remove it first and re-create it
log.Println("removing old branch protection for string : ", protectionPattern)
_, err = client.ProtectedBranches.UnprotectRepositoryBranches(projectID, protectionPattern)
if err != nil {
return fmt.Errorf("removing protection for existing branch failed : %v", err)
}
}

log.Println("re-creating branch protection for string ", protectionPattern)
if _, err = createBranchProtection(client, projectID, protectionPattern); err != nil {
return fmt.Errorf("recreating : %v", err)
}
return nil
}

func createBranchProtection(client *gitlab.Client, projectID int, name string) (*gitlab.ProtectedBranch, error) {
protectedBranch, _, err := client.ProtectedBranches.ProtectRepositoryBranches(projectID, &gitlab.ProtectRepositoryBranchesOptions{
Name: gitlab.String(name),
PushAccessLevel: gitlab.AccessLevel(gitlab.NoPermissions),
MergeAccessLevel: gitlab.AccessLevel(gitlab.MaintainerPermissions),
UnprotectAccessLevel: nil,
AllowForcePush: gitlab.Bool(false),
AllowedToPush: nil,
AllowedToMerge: nil,
AllowedToUnprotect: nil,
CodeOwnerApprovalRequired: nil,
})
if err != nil {
return nil, fmt.Errorf("creating new branch protection failed : %v", err)
}
return protectedBranch, nil
}

func isProtectedBranchSet(protectedBranch *gitlab.ProtectedBranch) bool {
//log.Println("checking branch protection for : ", spew.Sdump(protectedBranch))
if protectedBranch.AllowForcePush {
return false
}

if len(protectedBranch.PushAccessLevels) != 1 {
return false
}

if protectedBranch.PushAccessLevels[0].AccessLevel != gitlab.NoPermissions {
return false
}

if len(protectedBranch.MergeAccessLevels) != 1 {
return false
}

if protectedBranch.MergeAccessLevels[0].AccessLevel != gitlab.MaintainerPermissions {
return false
}

if len(protectedBranch.UnprotectAccessLevels) != 1 {
return false
}

if protectedBranch.UnprotectAccessLevels[0].AccessLevel != gitlab.MaintainerPermissions {
return false
}

return true
}

// finds the default branch for the given project
func getDefaultBranch(client *gitlab.Client, projectID int) (*gitlab.Branch, error) {
project, _, err := client.Projects.GetProject(projectID, &gitlab.GetProjectOptions{})
if err != nil {
return nil, fmt.Errorf("fetching project failed : %v", err)
}

defaultBranch := project.DefaultBranch

// first try with the possible option
branch, _, err := client.Branches.GetBranch(projectID, defaultBranch)
if err != nil {
return nil, fmt.Errorf("fetching default branch failed : %v", err)
}

return branch, nil
}
120 changes: 120 additions & 0 deletions cla-backend-go/gitlab_api/branch_protection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT

package gitlab

import (
"context"
"fmt"

log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
"github.com/xanzy/go-gitlab"
)

// SetOrCreateBranchProtection sets the required parameters if existing pattern exists or creates a new one
func SetOrCreateBranchProtection(ctx context.Context, client *gitlab.Client, projectID int, protectionPattern string) error {
var err error
f := logrus.Fields{
"functionName": "gitlab_api.SetOrCreateBranchProtection",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitlabProjectID": projectID,
"protectionPattern": protectionPattern,
}

log.WithFields(f).Debugf("setting branch protection...")

protectedBranch, resp, err := client.ProtectedBranches.GetProtectedBranch(projectID, protectionPattern)
if err != nil && resp.StatusCode != 404 {
return fmt.Errorf("fetching existing branch failed : %v", err)
}

if protectedBranch != nil {
if isProtectedBranchSet(protectedBranch) {
log.WithFields(f).Debugf("branch protection already set nothing to do")
return nil
}

//it's an existing one try to remove it first and re-create it
log.WithFields(f).Debugf("removing old branch protection")
_, err = client.ProtectedBranches.UnprotectRepositoryBranches(projectID, protectionPattern)
if err != nil {
return fmt.Errorf("removing protection for existing branch failed : %v", err)
}
}

log.WithFields(f).Debugf("re-creating branch protection ")
if _, err = createBranchProtection(client, projectID, protectionPattern); err != nil {
return fmt.Errorf("recreating branch protection failed : %v", err)
}
return nil
}

func createBranchProtection(client *gitlab.Client, projectID int, name string) (*gitlab.ProtectedBranch, error) {
protectedBranch, _, err := client.ProtectedBranches.ProtectRepositoryBranches(projectID, &gitlab.ProtectRepositoryBranchesOptions{
Name: gitlab.String(name),
PushAccessLevel: gitlab.AccessLevel(gitlab.NoPermissions),
MergeAccessLevel: gitlab.AccessLevel(gitlab.MaintainerPermissions),
UnprotectAccessLevel: nil,
AllowForcePush: gitlab.Bool(false),
AllowedToPush: nil,
AllowedToMerge: nil,
AllowedToUnprotect: nil,
CodeOwnerApprovalRequired: nil,
})
if err != nil {
return nil, fmt.Errorf("creating new branch protection failed : %v", err)
}
return protectedBranch, nil
}

func isProtectedBranchSet(protectedBranch *gitlab.ProtectedBranch) bool {
if protectedBranch.AllowForcePush {
return false
}

if len(protectedBranch.PushAccessLevels) != 1 {
return false
}

if protectedBranch.PushAccessLevels[0].AccessLevel != gitlab.NoPermissions {
return false
}

if len(protectedBranch.MergeAccessLevels) != 1 {
return false
}

if protectedBranch.MergeAccessLevels[0].AccessLevel != gitlab.MaintainerPermissions {
return false
}

if len(protectedBranch.UnprotectAccessLevels) != 1 {
return false
}

if protectedBranch.UnprotectAccessLevels[0].AccessLevel != gitlab.MaintainerPermissions {
return false
}

return true
}

// GetDefaultBranch finds the default branch for the given project
func GetDefaultBranch(client *gitlab.Client, projectID int) (*gitlab.Branch, error) {
project, _, err := client.Projects.GetProject(projectID, &gitlab.GetProjectOptions{})
if err != nil {
return nil, fmt.Errorf("fetching project failed : %v", err)
}

defaultBranch := project.DefaultBranch

// first try with the possible option
branch, _, err := client.Branches.GetBranch(projectID, defaultBranch)
if err != nil {
return nil, fmt.Errorf("fetching default branch failed : %v", err)
}

return branch, nil
}
1 change: 1 addition & 0 deletions cla-backend-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ require (
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8
github.com/ugorji/go v1.2.6 // indirect
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a
github.com/xanzy/go-gitlab v0.50.1
go.uber.org/ratelimit v0.1.0
Expand Down
Loading

0 comments on commit 742c4f4

Please sign in to comment.