Skip to content

Commit

Permalink
gitlab: Rewrite plugin to use new API client.
Browse files Browse the repository at this point in the history
- Use new teleport/api client.
- Add comments to the issue when new access review is submitted.
- Automatically change issue status once request is approved/denied.
- Manage plugin data state safely using compare-and-swap.
- Migrate tests to testify.

Closes #188.
Closes #209.
  • Loading branch information
marshall-lee committed Jul 6, 2021
1 parent 1c514b6 commit 04e9a2d
Show file tree
Hide file tree
Showing 12 changed files with 1,923 additions and 1,041 deletions.
620 changes: 436 additions & 184 deletions access/gitlab/app.go

Large diffs are not rendered by default.

417 changes: 0 additions & 417 deletions access/gitlab/bot.go

This file was deleted.

514 changes: 514 additions & 0 deletions access/gitlab/client.go

Large diffs are not rendered by default.

20 changes: 18 additions & 2 deletions access/gitlab/config.go
@@ -1,3 +1,19 @@
/*
Copyright 2020-2021 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
Expand Down Expand Up @@ -26,7 +42,7 @@ type Config struct {
type GitlabConfig struct {
URL string `toml:"url"`
Token string `toml:"token"`
ProjectID string `toml:"project_id"`
ProjectID IntID `toml:"project_id"`
WebhookSecret string `toml:"webhook_secret"`
}

Expand Down Expand Up @@ -91,7 +107,7 @@ func (c *Config) CheckAndSetDefaults() error {
if c.Gitlab.Token == "" {
return trace.BadParameter("missing required value gitlab.token")
}
if c.Gitlab.ProjectID == "" {
if c.Gitlab.ProjectID == 0 {
return trace.BadParameter("missing required value gitlab.project_id")
}
if c.Gitlab.WebhookSecret == "" {
Expand Down
103 changes: 55 additions & 48 deletions access/gitlab/database.go
@@ -1,3 +1,19 @@
/*
Copyright 2020-2021 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
Expand All @@ -15,124 +31,115 @@ const (
hookIDKey = "project-hook-id"
)

type DB struct {
*bolt.DB
projectID IntID
}

type Settings struct {
*bolt.Bucket
}

type Issues struct {
*bolt.Bucket
}
type DB struct{ *bolt.DB }
type SettingsBucket struct{ *bolt.Bucket }
type IssuesBucket struct{ *bolt.Bucket }

var ErrNoBucket = errors.New("No bucket created yet")

func OpenDB(path string, projectID IntID) (DB, error) {
func OpenDB(path string) (DB, error) {
db, err := bolt.Open(path, 0600, &bolt.Options{
Timeout: time.Second,
})
if err != nil {
return DB{}, trace.Wrap(err)
}
return DB{db, projectID}, nil
return DB{db}, nil
}

func (db *DB) projectBucketKey() []byte {
return []byte(fmt.Sprintf("project:%d", db.projectID))
func (db DB) projectBucketKey(projectID IntID) []byte {
return []byte(fmt.Sprintf("project:%d", projectID))
}

func (db *DB) updateProject(fn func(*bolt.Bucket) error) error {
func (db DB) updateProject(projectID IntID, fn func(*bolt.Bucket) error) error {
return db.Update(func(tx *bolt.Tx) error {
projectBucket, err := tx.CreateBucketIfNotExists(db.projectBucketKey())
projectBucket, err := tx.CreateBucketIfNotExists(db.projectBucketKey(projectID))
if err != nil {
return trace.Wrap(err)
}
return fn(projectBucket)
})
}

func (db *DB) viewProject(fn func(*bolt.Bucket) error) error {
func (db DB) viewProject(projectID IntID, fn func(*bolt.Bucket) error) error {
return db.View(func(tx *bolt.Tx) error {
projectBucket := tx.Bucket(db.projectBucketKey())
projectBucket := tx.Bucket(db.projectBucketKey(projectID))
if projectBucket == nil {
return trace.Wrap(ErrNoBucket)
}
return fn(projectBucket)
})
}

func (db *DB) UpdateSettings(fn func(Settings) error) error {
return db.updateProject(func(bucket *bolt.Bucket) error {
issuesBucket, err := bucket.CreateBucketIfNotExists([]byte(settingsBucketKey))
func (db DB) UpdateSettings(projectID IntID, fn func(SettingsBucket) error) error {
return db.updateProject(projectID, func(bucket *bolt.Bucket) error {
bucket, err := bucket.CreateBucketIfNotExists([]byte(settingsBucketKey))
if err != nil {
return trace.Wrap(err)
}
return fn(Settings{issuesBucket})
return fn(SettingsBucket{bucket})
})
}

func (db *DB) ViewSettings(fn func(Settings) error) error {
return db.viewProject(func(bucket *bolt.Bucket) error {
settingsBucket := bucket.Bucket([]byte(settingsBucketKey))
if settingsBucket == nil {
func (db DB) ViewSettings(projectID IntID, fn func(SettingsBucket) error) error {
return db.viewProject(projectID, func(bucket *bolt.Bucket) error {
bucket = bucket.Bucket([]byte(settingsBucketKey))
if bucket == nil {
return trace.Wrap(ErrNoBucket)
}
return fn(Settings{settingsBucket})
return fn(SettingsBucket{bucket})
})
}

func (db *DB) UpdateIssues(fn func(Issues) error) error {
return db.updateProject(func(bucket *bolt.Bucket) error {
issuesBucket, err := bucket.CreateBucketIfNotExists([]byte(issuesBucketKey))
func (db DB) UpdateIssues(projectID IntID, fn func(IssuesBucket) error) error {
return db.updateProject(projectID, func(bucket *bolt.Bucket) error {
bucket, err := bucket.CreateBucketIfNotExists([]byte(issuesBucketKey))
if err != nil {
return trace.Wrap(err)
}
return fn(Issues{issuesBucket})
return fn(IssuesBucket{bucket})
})
}

func (db *DB) ViewIssues(fn func(Issues) error) error {
return db.viewProject(func(bucket *bolt.Bucket) error {
issuesBucket := bucket.Bucket([]byte(issuesBucketKey))
if issuesBucket == nil {
func (db DB) ViewIssues(projectID IntID, fn func(IssuesBucket) error) error {
return db.viewProject(projectID, func(bucket *bolt.Bucket) error {
bucket = bucket.Bucket([]byte(issuesBucketKey))
if bucket == nil {
return trace.Wrap(ErrNoBucket)
}
return fn(Issues{issuesBucket})
return fn(IssuesBucket{bucket})
})
}

func (s *Settings) HookID() IntID {
func (s SettingsBucket) HookID() IntID {
return BytesToIntID(s.Get([]byte(hookIDKey)))
}

func (s *Settings) SetHookID(id IntID) error {
func (s SettingsBucket) SetHookID(id IntID) error {
return s.Put([]byte(hookIDKey), IntIDToBytes(id))
}

func (s *Settings) labelKey(key string) []byte {
func (s SettingsBucket) labelKey(key string) []byte {
return []byte(fmt.Sprintf("label/%s:name", key))
}

func (s *Settings) GetLabel(key string) string {
func (s SettingsBucket) GetLabel(key string) string {
return string(s.Get(s.labelKey(key)))
}

func (s *Settings) SetLabel(key string, label string) error {
func (s SettingsBucket) SetLabel(key string, label string) error {
return s.Put(s.labelKey(key), []byte(label))
}

func (s *Settings) GetLabels(keys ...string) map[string]string {
func (s SettingsBucket) GetLabels(keys ...string) map[string]string {
mapping := make(map[string]string)
for _, key := range keys {
mapping[key] = s.GetLabel(key)
}
return mapping
}

func (s *Settings) SetLabels(mapping map[string]string) error {
func (s SettingsBucket) SetLabels(mapping map[string]string) error {
for key, value := range mapping {
if err := s.SetLabel(key, value); err != nil {
return trace.Wrap(err)
Expand All @@ -141,14 +148,14 @@ func (s *Settings) SetLabels(mapping map[string]string) error {
return nil
}

func (i *Issues) requestIDKey(issueID IntID) []byte {
func (i IssuesBucket) requestIDKey(issueID IntID) []byte {
return []byte(fmt.Sprintf("%s:request-id", issueID))
}

func (i *Issues) GetRequestID(issueID IntID) string {
func (i IssuesBucket) GetRequestID(issueID IntID) string {
return string(i.Get(i.requestIDKey(issueID)))
}

func (i *Issues) SetRequestID(issueID IntID, reqID string) error {
func (i IssuesBucket) SetRequestID(issueID IntID, reqID string) error {
return i.Put(i.requestIDKey(issueID), []byte(reqID))
}

0 comments on commit 04e9a2d

Please sign in to comment.