/pkg/server/biz/scm/scm.go

https://github.com/caicloud/cyclone · Go · 192 lines · 121 code · 30 blank · 41 comment · 29 complexity · ad5854dabf280690019d22853feafb16 MD5 · raw file

  1. /*
  2. Copyright 2017 caicloud authors. All rights reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package scm
  14. import (
  15. "fmt"
  16. "strings"
  17. "github.com/caicloud/nirvana/log"
  18. c_v1alpha1 "github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
  19. "github.com/caicloud/cyclone/pkg/server/apis/v1alpha1"
  20. "github.com/caicloud/cyclone/pkg/util/cerr"
  21. )
  22. type newProviderFunc func(source *v1alpha1.SCMSource) (Provider, error)
  23. // scmProviders represents the set of SCM providers.
  24. var scmProviders map[v1alpha1.SCMType]newProviderFunc
  25. func init() {
  26. scmProviders = make(map[v1alpha1.SCMType]newProviderFunc)
  27. }
  28. // RegisterProvider registers SCM providers.
  29. func RegisterProvider(scmType v1alpha1.SCMType, pFunc newProviderFunc) error {
  30. if _, ok := scmProviders[scmType]; ok {
  31. return fmt.Errorf("scm provider %s already exists", scmType)
  32. }
  33. scmProviders[scmType] = pFunc
  34. return nil
  35. }
  36. const (
  37. // PullRequestStateOpen represents the open state of pull request.
  38. PullRequestStateOpen string = "open"
  39. )
  40. // Provider represents the interface of SCM provider.
  41. type Provider interface {
  42. GetToken() (string, error)
  43. ListRepos() ([]Repository, error)
  44. // ListBranches list branches of repo, repo format must be {owner}/{repo}.
  45. ListBranches(repo string) ([]string, error)
  46. // ListTags list tags of repo, repo format must be {owner}/{repo}.
  47. ListTags(repo string) ([]string, error)
  48. // ListPullRequests list pull requests of repo, repo format must be {owner}/{repo}.
  49. ListPullRequests(repo, state string) ([]PullRequest, error)
  50. ListDockerfiles(repo string) ([]string, error)
  51. CreateStatus(status c_v1alpha1.StatusPhase, targetURL, repoURL, commitSHA string) error
  52. GetPullRequestSHA(repoURL string, number int) (string, error)
  53. CheckToken() error
  54. CreateWebhook(repo string, webhook *Webhook) error
  55. DeleteWebhook(repo string, webhookURL string) error
  56. }
  57. // GetSCMProvider gets the SCM provider by the type.
  58. func GetSCMProvider(scm *v1alpha1.SCMSource) (Provider, error) {
  59. if scm == nil {
  60. err := fmt.Errorf("SCM config is nil")
  61. log.Error(err)
  62. return nil, err
  63. }
  64. scmType := scm.Type
  65. pFunc, ok := scmProviders[scmType]
  66. if !ok {
  67. return nil, cerr.ErrorUnsupported.Error("SCM type", scmType)
  68. }
  69. return pFunc(scm)
  70. }
  71. // GenerateSCMToken generates the SCM token according to the config.
  72. // Make sure the type, server of the SCM is provided. If the SCM is Github, the username is required.
  73. // If the access token is provided, it should be checked whether has authority of repos.
  74. // Generate new token only when the username and password are provided at the same time.
  75. func GenerateSCMToken(config *v1alpha1.SCMSource) error {
  76. if config == nil {
  77. return fmt.Errorf("SCM config %s not found", config)
  78. }
  79. if config.Type == v1alpha1.SVN {
  80. return nil
  81. }
  82. if config.AuthType != v1alpha1.AuthTypePassword && config.AuthType != v1alpha1.AuthTypeToken {
  83. return cerr.ErrorUnsupported.Error("SCM auth type", config.AuthType)
  84. }
  85. // Trim suffix '/' of Gitlab server to ensure that the token can work, otherwise there will be 401 error.
  86. config.Server = strings.TrimSuffix(config.Server, "/")
  87. scmType := config.Type
  88. provider, err := GetSCMProvider(config)
  89. if err != nil {
  90. return err
  91. }
  92. var generatedToken string
  93. switch scmType {
  94. case v1alpha1.GitHub, v1alpha1.GitLab, v1alpha1.Bitbucket:
  95. // If username and password is provided, generate the new token.
  96. if len(config.User) != 0 && len(config.Password) != 0 {
  97. generatedToken, err = provider.GetToken()
  98. if err != nil {
  99. log.Errorf("fail to get SCM token for user %s as %s", config.User, err.Error())
  100. return err
  101. }
  102. }
  103. default:
  104. return cerr.ErrorUnsupported.Error("SCM type", scmType)
  105. }
  106. if generatedToken != "" && config.AuthType == v1alpha1.AuthTypePassword {
  107. config.Token = generatedToken
  108. }
  109. // Cleanup the password for security.
  110. config.Password = ""
  111. // recreate the scm provider and check auth
  112. provider, err = GetSCMProvider(config)
  113. if err != nil {
  114. return err
  115. }
  116. err = provider.CheckToken()
  117. if err != nil {
  118. return err
  119. }
  120. return nil
  121. }
  122. // Webhook represents the params for SCM webhook.
  123. type Webhook struct {
  124. Events []EventType
  125. URL string
  126. }
  127. // EventType represents event types of SCM.
  128. type EventType string
  129. const (
  130. // PullRequestEventType represents pull request events.
  131. PullRequestEventType EventType = "scm-pull-request"
  132. // PullRequestCommentEventType represents pull request comment events.
  133. PullRequestCommentEventType EventType = "scm-pull-request-comment"
  134. // PushEventType represents commit push events.
  135. PushEventType EventType = "scm-push"
  136. // TagReleaseEventType represents tag release events.
  137. TagReleaseEventType EventType = "scm-tag-release"
  138. // PostCommitEventType represents post commit events.
  139. PostCommitEventType EventType = "scm-post-commit"
  140. )
  141. // EventData represents the data parsed from SCM events.
  142. type EventData struct {
  143. Type EventType
  144. Repo string
  145. Ref string
  146. Branch string
  147. Comment string
  148. CommitSHA string
  149. ChangedFiles []string
  150. }
  151. // PullRequest describes pull requests of SCM repositories.
  152. type PullRequest struct {
  153. ID int `json:"id"`
  154. Title string `json:"title"`
  155. Description string `json:"description"`
  156. State string `json:"state"`
  157. // TargetBranch used for GitLab to indicate to which branch the merge-request should merge.
  158. TargetBranch string `json:"targetBranch"`
  159. }