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

GitOps plugin system and a "kanvas" plugin placeholder #973

Merged
merged 1 commit into from
Nov 17, 2023
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
7 changes: 7 additions & 0 deletions gitops_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

// GitOpsPlugin is the extension point for InteractorGitOps
// It is used to support various GitOps tools.
type GitOpsPlugin interface {
Prepare(pj DeployProject, phase string, branch string, user User, message string) (o GitOpsPrepareOutput, err error)
}
56 changes: 56 additions & 0 deletions gitops_plugin_kanvas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

// GitOpsPluginKanvas is a gocat gitops plugin to prepare
// deployments using kanvas.
// This is used when you want to use gocat as a workflow engine
// with a chatops interface, while using kanvas as a deployment tool.
//
// Unlike GitOpsPluginKustomize which uses gocat's builtin Git and GitHub support,
// GitOpsPluginKanvas uses kanvas's Git and GitHub support.
type GitOpsPluginKanvas struct {
github *GitHub
git *GitOperator
}

func NewGitOpsPluginKanvas(github *GitHub, git *GitOperator) GitOpsPlugin {
return &GitOpsPluginKanvas{github: github, git: git}
}

func (k GitOpsPluginKanvas) Prepare(pj DeployProject, phase string, branch string, assigner User, tag string) (o GitOpsPrepareOutput, err error) {
o.status = DeployStatusFail
if tag == "" {
ecr, err := CreateECRInstance()
if err != nil {
return o, err
}
tag, err = ecr.FindImageTagByRegexp(pj.ECRRegistryId(), pj.ECRRepository(), pj.ImageTagRegexp(), pj.TargetRegexp(), ImageTagVars{Branch: branch, Phase: phase})
if err != nil {
return o, err
}
}

// TODO Enhance kanvas to create a git commit and branch from the image tag
_ = tag

// TODO Do kanvas deployment using specific PR title and description

// TODO if the result code is "No difference", we should not create a pull request.
// if tag == currentTag {
// o.status = DeployStatusAlready
// return
// }

// TODO Enhance kanvas to support setting assigner for the pull request
// TODO Enhance kanvas to returning:
// - pull request id
// - pull request number
// - pull request head branch

o = GitOpsPrepareOutput{
// PullRequestID: prID,
// PullRequestNumber: prNum,
// Branch: prBranch,
// status: DeployStatusSuccess,
}
return
}
15 changes: 15 additions & 0 deletions gitops_plugin_kustomize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

// GitOpsPluginKustomize is a gocat gitops plugin to prepare
// deployments using kustomize and gocat's builtin Git and GitHub support.
// This is used when you want to use gocat as a workflow engine
// with a chatops interface, while using kustomize along with
// the gocat native features as a deployment tool.
type GitOpsPluginKustomize struct {
github *GitHub
git *GitOperator
}

func NewGitOpsPluginKustomize(github *GitHub, git *GitOperator) GitOpsPlugin {
return &GitOpsPluginKustomize{github: github, git: git}
}
16 changes: 14 additions & 2 deletions interactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,23 @@ type DeployUsecase interface {
}

type InteractorFactory struct {
kustomize InteractorKustomize
kanvas InteractorGitOps
kustomize InteractorGitOps
jenkins InteractorJenkins
job InteractorJob
lambda InteractorLambda
combine InteractorCombine
}

func NewInteractorFactory(c InteractorContext) InteractorFactory {
return InteractorFactory{kustomize: NewInteractorKustomize(c), jenkins: NewInteractorJenkins(c), job: NewInteractorJob(c), lambda: NewInteractorLambda(c), combine: NewInteractorCombine(c)}
return InteractorFactory{
kanvas: NewInteractorKanavs(c),
kustomize: NewInteractorKustomize(c),
jenkins: NewInteractorJenkins(c),
job: NewInteractorJob(c),
lambda: NewInteractorLambda(c),
combine: NewInteractorCombine(c),
}
}

func (i InteractorFactory) Get(pj DeployProject, phase string) DeployUsecase {
Expand All @@ -50,6 +58,8 @@ func (i InteractorFactory) Get(pj DeployProject, phase string) DeployUsecase {

func (i InteractorFactory) get(kind string) DeployUsecase {
switch kind {
case "kanvas":
return i.kanvas
case "kustomize":
return i.kustomize
case "job":
Expand All @@ -65,6 +75,8 @@ func (i InteractorFactory) get(kind string) DeployUsecase {

func (i InteractorFactory) GetByParams(params string) DeployUsecase {
switch {
case strings.Contains(params, "kanvas"):
return i.kanvas
case strings.Contains(params, "kustomize"):
return i.kustomize
case strings.Contains(params, "job"):
Expand Down
10 changes: 10 additions & 0 deletions interactor_kanvas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

func NewInteractorKanavs(i InteractorContext) (o InteractorGitOps) {
o = InteractorGitOps{
InteractorContext: i,
model: NewGitOpsPluginKanvas(&o.github, &o.git),
}
o.kind = "kanvas"
return
}
25 changes: 14 additions & 11 deletions interactor_kustomize.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ import (
"github.com/nlopes/slack"
)

type InteractorKustomize struct {
type InteractorGitOps struct {
InteractorContext
model ModelKustomize
model GitOpsPlugin
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we make it reusable; All the reusable logics reside within InteractorGitOps, and tool-specific logic resides within each GitOpsPlugin like GitOpsPluginKustomize and GitOpsPluginKanvas.

A InteractorGitOps instance along with GitOpsPluginKustomize works exactly same as the current InteractorKustomize, so this change is backward-compatible.

This is about Interactor. For Model, see https://github.com/zaiminc/gocat/pull/973/files#r1395058220

}

func NewInteractorKustomize(i InteractorContext) (o InteractorKustomize) {
o = InteractorKustomize{InteractorContext: i, model: NewModelKustomize(&o.github, &o.git)}
func NewInteractorKustomize(i InteractorContext) (o InteractorGitOps) {
o = InteractorGitOps{
InteractorContext: i,
model: NewGitOpsPluginKustomize(&o.github, &o.git),
}
o.kind = "kustomize"
return
}

func (i InteractorKustomize) Request(pj DeployProject, phase string, branch string, assigner string, channel string) (blocks []slack.Block, err error) {
func (i InteractorGitOps) Request(pj DeployProject, phase string, branch string, assigner string, channel string) (blocks []slack.Block, err error) {
user := i.userList.FindBySlackUserID(assigner)

go func() {
Expand Down Expand Up @@ -50,23 +53,23 @@ func (i InteractorKustomize) Request(pj DeployProject, phase string, branch stri
return i.plainBlocks("Now creating pull request..."), nil
}

func (i InteractorKustomize) BranchList(pj DeployProject, phase string) ([]slack.Block, error) {
func (i InteractorGitOps) BranchList(pj DeployProject, phase string) ([]slack.Block, error) {
return i.branchList(pj, phase)
}

func (i InteractorKustomize) BranchListFromRaw(params string) ([]slack.Block, error) {
func (i InteractorGitOps) BranchListFromRaw(params string) ([]slack.Block, error) {
p := strings.Split(params, "_")
pj := i.projectList.Find(p[0])
return i.branchList(pj, p[1])
}

func (i InteractorKustomize) SelectBranch(params string, branch string, userID string, channel string) ([]slack.Block, error) {
func (i InteractorGitOps) SelectBranch(params string, branch string, userID string, channel string) ([]slack.Block, error) {
p := strings.Split(params, "_")
pj := i.projectList.Find(p[0])
return i.Request(pj, p[1], branch, userID, channel)
}

func (i InteractorKustomize) Approve(params string, userID string, channel string) (blocks []slack.Block, err error) {
func (i InteractorGitOps) Approve(params string, userID string, channel string) (blocks []slack.Block, err error) {
prID := ""
prNumber := ""
p := strings.Split(params, "_")
Expand Down Expand Up @@ -113,12 +116,12 @@ func (i InteractorKustomize) Approve(params string, userID string, channel strin
return
}

func (i InteractorKustomize) Reject(params string, userID string) ([]slack.Block, error) {
func (i InteractorGitOps) Reject(params string, userID string) ([]slack.Block, error) {
p := strings.Split(params, "_")
return i.reject(p[0], p[1], p[2], userID)
}

func (i InteractorKustomize) reject(prID string, prNum string, branch string, userID string) (blocks []slack.Block, err error) {
func (i InteractorGitOps) reject(prID string, prNum string, branch string, userID string) (blocks []slack.Block, err error) {
if err = i.github.ClosePullRequest(prID); err != nil {
return
}
Expand Down
2 changes: 2 additions & 0 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func NewDeployModelList(github *GitHub, git *GitOperator, projectList *ProjectLi
return &DeployModelList{
"lambda": NewModelLambda(),
"kustomize": NewModelKustomize(github, git),
"kanvas": NewModelKanvas(github, git),
"combine": NewModelCombine(github, git, projectList),
"job": NewModelJob(github),
}
Expand All @@ -61,6 +62,7 @@ func NewDeployModelListWithoutCombine(github *GitHub, git *GitOperator) *DeployM
return &DeployModelList{
"lambda": NewModelLambda(),
"kustomize": NewModelKustomize(github, git),
"kanvas": NewModelKanvas(github, git),
"job": NewModelJob(github),
}
}
Expand Down
9 changes: 9 additions & 0 deletions model_kanvas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

func NewModelKanvas(github *GitHub, git *GitOperator) ModelGitOps {
return ModelGitOps{
github: github,
git: git,
plugin: NewGitOpsPluginKanvas(github, git),
}
}
37 changes: 21 additions & 16 deletions model_kustomize.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,36 @@ import (
"strings"
)

type ModelKustomize struct {
type ModelGitOps struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We turn the existing ModelKustomize to a reusable Model implementation, that is used for building the kustomize model and the kanvas model.

github *GitHub
git *GitOperator
plugin GitOpsPlugin
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we make it reusable; All the reusable logics reside within ModelGitOps, and tool-specific logic resides within each GitOpsPlugin like GitOpsPluginKustomize and GitOpsPluginKanvas.

A ModelGitOps instance along with GitOpsPluginKustomize works exactly same as the current ModelKustomize, so this change is backward-compatible.

}

func NewModelKustomize(github *GitHub, git *GitOperator) ModelKustomize {
return ModelKustomize{github: github, git: git}
func NewModelKustomize(github *GitHub, git *GitOperator) ModelGitOps {
return ModelGitOps{
github: github,
git: git,
plugin: NewGitOpsPluginKustomize(github, git),
}
}

type ModelKustomizePrepareOutput struct {
type GitOpsPrepareOutput struct {
PullRequestID string
PullRequestNumber int
Branch string
status DeployStatus
}

func (self ModelKustomizePrepareOutput) Status() DeployStatus {
func (self GitOpsPrepareOutput) Status() DeployStatus {
return self.status
}

func (self ModelKustomizePrepareOutput) Message() string {
func (self GitOpsPrepareOutput) Message() string {
return "Success to deploy"
}

func (self ModelKustomize) Prepare(pj DeployProject, phase string, branch string, assigner User, tag string) (o ModelKustomizePrepareOutput, err error) {
func (k GitOpsPluginKustomize) Prepare(pj DeployProject, phase string, branch string, assigner User, tag string) (o GitOpsPrepareOutput, err error) {
o.status = DeployStatusFail
if tag == "" {
ecr, err := CreateECRInstance()
Expand All @@ -43,7 +48,7 @@ func (self ModelKustomize) Prepare(pj DeployProject, phase string, branch string
}

ph := pj.FindPhase(phase)
currentTag, err := ph.Destination.GetCurrentRevision(GetCurrentRevisionInput{github: self.github})
currentTag, err := ph.Destination.GetCurrentRevision(GetCurrentRevisionInput{github: k.github})
if err != nil {
return
}
Expand All @@ -53,7 +58,7 @@ func (self ModelKustomize) Prepare(pj DeployProject, phase string, branch string
return
}

commits, err := self.github.CommitsBetween(GitHubCommitsBetweenInput{
commits, err := k.github.CommitsBetween(GitHubCommitsBetweenInput{
Repository: pj.GitHubRepository(),
Branch: branch,
FirstCommitID: currentTag,
Expand All @@ -66,24 +71,24 @@ func (self ModelKustomize) Prepare(pj DeployProject, phase string, branch string
commitlog = commitlog + "- " + m + "\n"
}

prBranch, err := self.git.PushDockerImageTag(pj.ID, ph, tag, pj.DockerRepository())
prBranch, err := k.git.PushDockerImageTag(pj.ID, ph, tag, pj.DockerRepository())
if err != nil {
return
}

prID, prNum, err := self.github.CreatePullRequest(prBranch, fmt.Sprintf("Deploy %s %s", pj.ID, branch), commitlog)
prID, prNum, err := k.github.CreatePullRequest(prBranch, fmt.Sprintf("Deploy %s %s", pj.ID, branch), commitlog)
if err != nil {
return
}

if assigner.GitHubNodeID != "" {
err = self.github.UpdatePullRequest(prID, assigner.GitHubNodeID)
err = k.github.UpdatePullRequest(prID, assigner.GitHubNodeID)
if err != nil {
return
}
}

o = ModelKustomizePrepareOutput{
o = GitOpsPrepareOutput{
PullRequestID: prID,
PullRequestNumber: prNum,
Branch: prBranch,
Expand All @@ -92,13 +97,13 @@ func (self ModelKustomize) Prepare(pj DeployProject, phase string, branch string
return
}

func (self ModelKustomize) Commit(pullRequestID string) error {
func (self ModelGitOps) Commit(pullRequestID string) error {
return self.github.MergePullRequest(pullRequestID)

}

func (self ModelKustomize) Deploy(pj DeployProject, phase string, option DeployOption) (do DeployOutput, err error) {
o, err := self.Prepare(pj, phase, option.Branch, option.Assigner, option.Tag)
func (self ModelGitOps) Deploy(pj DeployProject, phase string, option DeployOption) (do DeployOutput, err error) {
o, err := self.plugin.Prepare(pj, phase, option.Branch, option.Assigner, option.Tag)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy works in an implementation-specific way so we call out to the plugin's Prepare here.

This is the only implementation-specific part of ModelGitOps, and therefore this is the only place you see self.plugin.

For example, Commit above works regardless of the tool(kanvas, kustomize, and so on) so it doesn't call out to a plugin = no self.plugin usage.

if err != nil {
return
}
Expand Down
Loading