| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- package gitlab
- import (
- "fmt"
- "net/http"
- "strings"
- "github.com/porter-dev/porter/api/server/shared/commonutils"
- "github.com/porter-dev/porter/api/server/shared/config"
- "github.com/porter-dev/porter/internal/oauth"
- "github.com/porter-dev/porter/internal/repository"
- "github.com/xanzy/go-gitlab"
- "gopkg.in/yaml.v2"
- )
- type GitlabCI struct {
- ServerURL string
- GitRepoName string
- GitRepoOwner string
- Repo repository.Repository
- ProjectID uint
- ClusterID uint
- UserID uint
- IntegrationID uint
- PorterConf *config.Config
- ReleaseName string
- ReleaseNamespace string
- FolderPath string
- PorterToken string
- defaultGitBranch string
- pID string
- }
- func (g *GitlabCI) Setup() error {
- client, err := g.getClient()
- if err != nil {
- return err
- }
- g.pID = fmt.Sprintf("%s/%s", g.GitRepoOwner, g.GitRepoName)
- branches, _, err := client.Branches.ListBranches(g.pID, &gitlab.ListBranchesOptions{})
- if err != nil {
- return fmt.Errorf("error fetching list of branches: %w", err)
- }
- for _, branch := range branches {
- if branch.Default {
- g.defaultGitBranch = branch.Name
- break
- }
- }
- err = g.createGitlabSecret(client)
- if err != nil {
- return err
- }
- jobName := getGitlabStageJobName(g.ReleaseName)
- ciFile, resp, err := client.RepositoryFiles.GetFile(g.pID, ".gitlab-ci.yml", &gitlab.GetFileOptions{
- Ref: gitlab.String(g.defaultGitBranch),
- })
- if resp.StatusCode == http.StatusNotFound {
- // create .gitlab-ci.yml
- contentsMap := make(map[string]interface{})
- contentsMap["stages"] = []string{
- jobName,
- }
- contentsMap[jobName] = g.getCIJob(jobName)
- contentsYAML, _ := yaml.Marshal(contentsMap)
- _, _, err = client.RepositoryFiles.CreateFile(g.pID, ".gitlab-ci.yml", &gitlab.CreateFileOptions{
- Branch: gitlab.String(g.defaultGitBranch),
- AuthorName: gitlab.String("Porter Bot"),
- AuthorEmail: gitlab.String("contact@getporter.dev"),
- Content: gitlab.String(string(contentsYAML)),
- CommitMessage: gitlab.String("Create .gitlab-ci.yml file"),
- })
- if err != nil {
- return fmt.Errorf("error creating .gitlab-ci.yml file: %w", err)
- }
- } else if err != nil {
- return fmt.Errorf("error getting .gitlab-ci.yml file: %w", err)
- } else {
- // update .gitlab-ci.yml if needed
- ciFileContentsMap := make(map[string]interface{})
- err = yaml.Unmarshal([]byte(ciFile.Content), ciFileContentsMap)
- if err != nil {
- return fmt.Errorf("error unmarshalling existing .gitlab-ci.yml: %w", err)
- }
- stages, ok := ciFileContentsMap["stages"].([]string)
- if !ok {
- return fmt.Errorf("error converting stages to string slice")
- }
- stageExists := false
- for _, stage := range stages {
- if stage == jobName {
- stageExists = true
- break
- }
- }
- if !stageExists {
- stages = append(stages, jobName)
- ciFileContentsMap["stages"] = stages
- }
- ciFileContentsMap[jobName] = g.getCIJob(jobName)
- contentsYAML, _ := yaml.Marshal(ciFileContentsMap)
- _, _, err = client.RepositoryFiles.UpdateFile(g.pID, ".gitlab-ci.yml", &gitlab.UpdateFileOptions{
- Branch: gitlab.String(g.defaultGitBranch),
- AuthorName: gitlab.String("Porter Bot"),
- AuthorEmail: gitlab.String("contact@getporter.dev"),
- Content: gitlab.String(string(contentsYAML)),
- CommitMessage: gitlab.String("Update .gitlab-ci.yml file"),
- })
- if err != nil {
- return fmt.Errorf("error updating .gitlab-ci.yml file to add porter job: %w", err)
- }
- }
- return nil
- }
- func (g *GitlabCI) Cleanup() error {
- client, err := g.getClient()
- if err != nil {
- return err
- }
- g.pID = fmt.Sprintf("%s/%s", g.GitRepoOwner, g.GitRepoName)
- branches, _, err := client.Branches.ListBranches(g.pID, &gitlab.ListBranchesOptions{})
- if err != nil {
- return fmt.Errorf("error fetching list of branches: %w", err)
- }
- for _, branch := range branches {
- if branch.Default {
- g.defaultGitBranch = branch.Name
- break
- }
- }
- err = g.deleteGitlabSecret(client)
- if err != nil {
- return err
- }
- jobName := getGitlabStageJobName(g.ReleaseName)
- ciFile, resp, err := client.RepositoryFiles.GetFile(g.pID, ".gitlab-ci.yml", &gitlab.GetFileOptions{
- Ref: gitlab.String(g.defaultGitBranch),
- })
- if resp.StatusCode == http.StatusNotFound {
- return nil
- } else if err != nil {
- return fmt.Errorf("error getting .gitlab-ci.yml file: %w", err)
- }
- ciFileContentsMap := make(map[string]interface{})
- err = yaml.Unmarshal([]byte(ciFile.Content), ciFileContentsMap)
- if err != nil {
- return fmt.Errorf("error unmarshalling existing .gitlab-ci.yml: %w", err)
- }
- stages, ok := ciFileContentsMap["stages"].([]string)
- if !ok {
- return fmt.Errorf("error converting stages to string slice")
- }
- var newStages []string
- for _, stage := range stages {
- if stage != jobName {
- newStages = append(newStages, stage)
- }
- }
- ciFileContentsMap["stage"] = newStages
- delete(ciFileContentsMap, jobName)
- contentsYAML, _ := yaml.Marshal(ciFileContentsMap)
- _, _, err = client.RepositoryFiles.UpdateFile(g.pID, ".gitlab-ci.yml", &gitlab.UpdateFileOptions{
- Branch: gitlab.String(g.defaultGitBranch),
- AuthorName: gitlab.String("Porter Bot"),
- AuthorEmail: gitlab.String("contact@getporter.dev"),
- Content: gitlab.String(string(contentsYAML)),
- CommitMessage: gitlab.String("Update .gitlab-ci.yml file"),
- })
- if err != nil {
- return fmt.Errorf("error updating .gitlab-ci.yml file to remove porter job: %w", err)
- }
- return nil
- }
- func (g *GitlabCI) getClient() (*gitlab.Client, error) {
- gi, err := g.Repo.GitlabIntegration().ReadGitlabIntegration(g.ProjectID, g.IntegrationID)
- if err != nil {
- return nil, err
- }
- oauthInt, err := g.Repo.GitlabAppOAuthIntegration().ReadGitlabAppOAuthIntegration(g.UserID, g.ProjectID, g.IntegrationID)
- if err != nil {
- return nil, err
- }
- accessToken, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, commonutils.GetGitlabOAuthConf(g.PorterConf, gi),
- oauth.MakeUpdateGitlabAppOAuthIntegrationFunction(oauthInt, g.Repo))
- if err != nil {
- return nil, err
- }
- client, err := gitlab.NewOAuthClient(accessToken, gitlab.WithBaseURL(gi.InstanceURL))
- if err != nil {
- return nil, err
- }
- return client, nil
- }
- func (g *GitlabCI) getCIJob(jobName string) map[string]interface{} {
- return map[string]interface{}{
- "image": "public.ecr.aws/o1j4x7p4/porter-cli:latest",
- "stage": jobName,
- "timeout": "20 minutes",
- "variables": map[string]string{
- "GIT_STRATEGY": "clone",
- },
- "script": []string{
- fmt.Sprintf("export PORTER_HOST=\"%s\"", g.ServerURL),
- fmt.Sprintf("export PORTER_PROJECT=\"%d\"", g.ProjectID),
- fmt.Sprintf("export PORTER_CLUSTER=\"%d\"", g.ClusterID),
- fmt.Sprintf("export PORTER_TOKEN=\"$%s\"", g.getPorterTokenSecretName()),
- "export PORTER_TAG=\"$(echo $CI_COMMIT_SHA | cut -c1-7)\"",
- fmt.Sprintf("porter update --app \"%s\" --tag \"$PORTER_TAG\" --namespace \"%s\" --path \"%s\" --stream",
- g.ReleaseName, g.ReleaseNamespace, g.FolderPath),
- },
- }
- }
- func (g *GitlabCI) createGitlabSecret(client *gitlab.Client) error {
- _, _, err := client.ProjectVariables.CreateVariable(g.pID, &gitlab.CreateProjectVariableOptions{
- Key: gitlab.String(g.getPorterTokenSecretName()),
- Value: gitlab.String(g.PorterToken),
- Masked: gitlab.Bool(true),
- })
- if err != nil {
- return fmt.Errorf("error creating porter token variable: %w", err)
- }
- return nil
- }
- func (g *GitlabCI) deleteGitlabSecret(client *gitlab.Client) error {
- _, err := client.ProjectVariables.RemoveVariable(g.pID, g.getPorterTokenSecretName(), &gitlab.RemoveProjectVariableOptions{})
- if err != nil {
- return fmt.Errorf("error removing porter token variable: %w", err)
- }
- return nil
- }
- func (g *GitlabCI) getPorterTokenSecretName() string {
- return fmt.Sprintf("PORTER_TOKEN_%d", g.ProjectID)
- }
- func getGitlabStageJobName(releaseName string) string {
- return fmt.Sprintf("porter-%s", strings.ToLower(strings.ReplaceAll(releaseName, "_", "-")))
- }
|