Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Trigger Actions and Add Ability To Kick Off Runs With Specified Terraform Version #20

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## TFBuddy Settings
#TFBUDDY_LOG_LEVEL=INFO
TFBUDDY_HOOK_SECRET=asdf

## Terraform Cloud Settings
TFC_ORGANIZATION=*SOME_ORG_HERE*
TFC_TOKEN=*YOUR_TFC_TOKEN_HERE*

## GITLAB Settings
GITLAB_TOKEN=*YOUR_GITLAB_TOKEN_HERE*

## GITHUB Settings
GITHUB_TOKEN=*YOUR_GITHUB_TOKEN_HERE*
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# asdf plugin add earthly https://github.com/YR-ZR0/asdf-earthly
earthly 0.6.30
golang 1.19.3
kustomize 4.5.7
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ docker:
docker build -t tfbuddy:dev -f Dockerfile.server ./

minikube-up:
minikube start --driver=hyperkit
minikube start --driver=docker
minikube addons enable ingress
minikube addons enable registry
echo "Remember to run minikube tunnel on mac before running localdev"

tunnel:
minikube tunnel

setup-localdev:
tilt up

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ TFBuddy consists of the webhook handler and a NATS cluster.

See [Installation Docs](https://tfbuddy.readthedocs.io/en/stable/usage/)

## Contributing

The [contributing](https://tfbuddy.readthedocs.io/en/stable/contributing/) has everything you need to start working on TFBuddy.


## Documentation

Expand Down
2 changes: 1 addition & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ cfgInputs = {
'TFBUDDY_LOG_LEVEL': 'debug',
'TFBUDDY_DEFAULT_TFC_ORGANIZATION' : os.getenv('TFC_ORGANIZATION'),
'TFBUDDY_WORKSPACE_ALLOW_LIST' : tfcOutputs.setdefault('tfc_workspace', '') if tfcOutputs else '',
'GITLAB_TOKEN_USER' : os.getenv('GITLAB_TOKEN_USER'),
'GITLAB_TOKEN' : os.getenv('GITLAB_TOKEN'),
}
if cfg.get('enable_gitlab') and gitlabOutputs:
cfgInputs.update({'TFBUDDY_PROJECT_ALLOW_LIST': gitlabOutputs.setdefault('gitlab_project_name', '')})
Expand Down
2 changes: 0 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,10 @@ helm install tfbuddy charts/tfbuddy \
```console
export TFC_TOKEN="" \
GITLAB_TOKEN="" \
GITLAB_TOKEN_USER=""

helm install tfbuddy charts/tfbuddy \
--set secrets.env.TFC_TOKEN="${TFC_TOKEN}" \
--set secrets.env.GITLAB_TOKEN="${GITLAB_TOKEN}" \
--set secrets.env.GITLAB_TOKEN_USER="${GITLAB_TOKEN_USER}" \
--dependency-update
```

Expand Down
26 changes: 20 additions & 6 deletions pkg/comment_actions/parsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ import (

"github.com/jessevdk/go-flags"
"github.com/rs/zerolog/log"
"github.com/zapier/tfbuddy/pkg/tfc_trigger"
"github.com/zapier/tfbuddy/pkg/utils"
)

var (
ErrNotTFCCommand = errors.New("not a TFC command")
ErrOtherTFTool = errors.New("Use 'tfc' to interact with tfbuddy.")
ErrOtherTFTool = errors.New("use 'tfc' to interact with tfbuddy")
ErrNoNotePassed = errors.New("no notes passed in note block")
ErrInvalidAction = errors.New("invalid tfc action")
)

type CommentOpts struct {
Args CommentArgs `positional-args:"yes" required:"yes"`
Workspace string `short:"w" long:"workspace" description:"A specific terraform Workspace to use" required:"false"`
TriggerOpts *tfc_trigger.TFCTriggerOptions
Args CommentArgs `positional-args:"yes" required:"yes"`
}

type CommentArgs struct {
Expand All @@ -28,14 +31,20 @@ type CommentArgs struct {

func ParseCommentCommand(noteBody string) (*CommentOpts, error) {
comment := strings.TrimSpace(strings.ToLower(noteBody))

words := strings.Fields(comment)
if len(words) < 2 || len(words) > 4 {

if len(words) == 0 {
return nil, ErrNoNotePassed
}

if len(words)%2 != 0 {
log.Debug().Str("comment", comment[0:10]).Msg("not a tfc command")
sl1pm4t marked this conversation as resolved.
Show resolved Hide resolved
return nil, ErrNotTFCCommand
}

opts := &CommentOpts{}
opts := &CommentOpts{
TriggerOpts: &tfc_trigger.TFCTriggerOptions{},
}
_, err := flags.ParseArgs(opts, words)
if err != nil {
log.Error().Err(err).Msg("error parsing comment as command")
Expand All @@ -50,5 +59,10 @@ func ParseCommentCommand(noteBody string) (*CommentOpts, error) {
return nil, ErrNotTFCCommand
}

opts.TriggerOpts.Action = tfc_trigger.CheckTriggerAction(opts.Args.Command)
if opts.TriggerOpts.Action == tfc_trigger.InvalidAction {
return nil, ErrInvalidAction
}

return opts, nil
}
72 changes: 72 additions & 0 deletions pkg/comment_actions/parsing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package comment_actions

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/zapier/tfbuddy/pkg/tfc_trigger"
)

func TestParseCommentCommand(t *testing.T) {
tcs := []struct {
noteBody string
expectedOpts *CommentOpts
e error
testName string
}{
{"", nil, ErrNoNotePassed, "empty test"},
{"tfc apply", &CommentOpts{
TriggerOpts: &tfc_trigger.TFCTriggerOptions{
Action: tfc_trigger.ApplyAction,
},
Args: CommentArgs{
Agent: "tfc",
Command: "apply",
},
}, nil, "simple plan"},
{"tfc plan -w fake_space", &CommentOpts{
TriggerOpts: &tfc_trigger.TFCTriggerOptions{
Workspace: "fake_space",
Action: tfc_trigger.PlanAction,
},
Args: CommentArgs{
Agent: "tfc",
Command: "plan",
},
}, nil, "simple plan with workspace"},
{"tfc apply -w fake_space -v 1.1.7", &CommentOpts{
TriggerOpts: &tfc_trigger.TFCTriggerOptions{
Workspace: "fake_space",
TFVersion: "1.1.7",
Action: tfc_trigger.ApplyAction,
},
Args: CommentArgs{
Agent: "tfc",
Command: "apply",
},
}, nil, "simple plan with workspace and version"},
{"tfc plan -v 1.1.8", &CommentOpts{
TriggerOpts: &tfc_trigger.TFCTriggerOptions{
TFVersion: "1.1.8",
Action: tfc_trigger.PlanAction,
},
Args: CommentArgs{
Agent: "tfc",
Command: "plan",
},
}, nil, "simple plan with version"},
{"tfc plan -w -v 1.1.8",
nil,
ErrNotTFCCommand,
"not a valid command",
},
}

for _, tc := range tcs {
mplachter marked this conversation as resolved.
Show resolved Hide resolved
t.Run(tc.testName, func(t *testing.T) {
opts, err := ParseCommentCommand(tc.noteBody)
assert.Equal(t, tc.expectedOpts, opts, tc.testName)
assert.ErrorIs(t, err, tc.e, tc.testName)
})
}
}
2 changes: 1 addition & 1 deletion pkg/github/hooks/github_hooks_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type TriggerCreationFunc func(
vcs vcs.GitClient,
tfc tfc_api.ApiClient,
runstream runstream.StreamClient,
cfg tfc_trigger.TriggerConfig,
cfg *tfc_trigger.TFCTriggerOptions,
) tfc_trigger.Trigger

type GithubHooksHandler struct {
Expand Down
35 changes: 14 additions & 21 deletions pkg/github/hooks/stream_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,20 @@ func (h *GithubHooksHandler) processIssueComment(msg *GithubIssueCommentEventMsg
}
pullReq := pr.(*github.GithubPR)

trigger := h.triggerCreation(h.vcs, h.tfc, h.runstream,
&tfc_trigger.TFCTriggerConfig{
Branch: pr.GetSourceBranch(),
CommitSHA: pullReq.GetBase().GetSHA(),
ProjectNameWithNamespace: event.GetRepo().GetFullName(),
MergeRequestIID: *event.Issue.Number,
TriggerSource: tfc_trigger.CommentTrigger,
VcsProvider: "github",
})
opts.TriggerOpts.Branch = pr.GetSourceBranch()
opts.TriggerOpts.CommitSHA = pullReq.GetBase().GetSHA()
opts.TriggerOpts.ProjectNameWithNamespace = event.GetRepo().GetFullName()
opts.TriggerOpts.MergeRequestIID = *event.Issue.Number
opts.TriggerOpts.TriggerSource = tfc_trigger.CommentTrigger
opts.TriggerOpts.VcsProvider = "github"

cfg, err := tfc_trigger.NewTFCTriggerConfig(opts.TriggerOpts)
if err != nil {
log.Error().Err(err).Msg("could not create TFCTriggerConfig")
return err
}

trigger := h.triggerCreation(h.vcs, h.tfc, h.runstream, cfg)

//// TODO: support additional commands and arguments (e.g. destroy, refresh, lock, unlock)
//// TODO: this should be refactored and be agnostic to the VCS type
Expand All @@ -88,24 +93,12 @@ func (h *GithubHooksHandler) processIssueComment(msg *GithubIssueCommentEventMsg
h.postPullRequestComment(event, ":no_entry: Apply failed. Pull Request has conflicts that need to be resolved.")
return nil
}
trigger.GetConfig().SetAction(tfc_trigger.ApplyAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

case "lock":
log.Info().Msg("Got TFC lock command")
trigger.GetConfig().SetAction(tfc_trigger.LockAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

case "plan":
log.Info().Msg("Got TFC plan command")
trigger.GetConfig().SetAction(tfc_trigger.PlanAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

case "unlock":
log.Info().Msg("Got TFC unlock command")
trigger.GetConfig().SetAction(tfc_trigger.UnlockAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

default:
return fmt.Errorf("could not parse command")
}
Expand Down
1 change: 1 addition & 0 deletions pkg/gitlab/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func NewGitlabClient() *GitlabClient {
return nil
}
}
//TODO: I believe this is legacy and can be removed?
tokenUser := os.Getenv("GITLAB_TOKEN_USER")
if token == "" {
if token == "" {
Expand Down
37 changes: 15 additions & 22 deletions pkg/gitlab_hooks/comment_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,23 @@ func (w *GitlabEventWorker) processNoteEvent(event vcs.MRCommentEvent) (projectN
return proj, err
}

trigger := w.triggerCreation(w.gl, w.tfc, w.runstream,
&tfc_trigger.TFCTriggerConfig{
Branch: event.GetMR().GetSourceBranch(),
CommitSHA: event.GetLastCommit().GetSHA(),
ProjectNameWithNamespace: proj,
MergeRequestIID: event.GetMR().GetInternalID(),
TriggerSource: tfc_trigger.CommentTrigger,
VcsProvider: "gitlab",
})
opts.TriggerOpts.Branch = event.GetMR().GetSourceBranch()
opts.TriggerOpts.CommitSHA = event.GetLastCommit().GetSHA()
opts.TriggerOpts.ProjectNameWithNamespace = proj
opts.TriggerOpts.MergeRequestIID = event.GetMR().GetInternalID()
opts.TriggerOpts.TriggerSource = tfc_trigger.CommentTrigger
opts.TriggerOpts.VcsProvider = "gitlab"

cfg, err := tfc_trigger.NewTFCTriggerConfig(opts.TriggerOpts)
if err != nil {
log.Error().Err(err).Msg("could not create TFCTriggerConfig")
return proj, err
}

trigger := w.triggerCreation(w.gl, w.tfc, w.runstream, cfg)

if event.GetAttributes().GetType() == string(gitlab.DiscussionNote) {
trigger.GetConfig().SetMergeRequestDiscussionID(event.GetAttributes().GetDiscussionID())
trigger.SetMergeRequestDiscussionID(event.GetAttributes().GetDiscussionID())
}

// TODO: support additional commands and arguments (e.g. destroy, refresh, lock, unlock)
Expand All @@ -60,24 +65,12 @@ func (w *GitlabEventWorker) processNoteEvent(event vcs.MRCommentEvent) (projectN
w.postMessageToMergeRequest(event, ":no_entry: Apply failed. Merge Request has conflicts that need to be resolved.")
return proj, nil
}
trigger.GetConfig().SetAction(tfc_trigger.ApplyAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

case "lock":
log.Info().Msg("Got TFC lock command")
trigger.GetConfig().SetAction(tfc_trigger.LockAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

case "plan":
log.Info().Msg("Got TFC plan command")
trigger.GetConfig().SetAction(tfc_trigger.PlanAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

case "unlock":
log.Info().Msg("Got TFC unlock command")
trigger.GetConfig().SetAction(tfc_trigger.UnlockAction)
trigger.GetConfig().SetWorkspace(opts.Workspace)

default:
return proj, nil
}
Expand Down
Loading