/
scm.go
185 lines (150 loc) · 5.08 KB
/
scm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
Copyright 2017 caicloud authors. All rights reserved.
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 scm
import (
"fmt"
"strings"
log "github.com/golang/glog"
"github.com/caicloud/cyclone/pkg/api"
httperror "github.com/caicloud/cyclone/pkg/util/http/errors"
wscm "github.com/caicloud/cyclone/pkg/worker/scm"
)
type newSCMProviderFunc func(*api.SCMConfig) (SCMProvider, error)
// scmProviders represents the set of SCM providers.
var scmProviders map[api.SCMType]newSCMProviderFunc
func init() {
scmProviders = make(map[api.SCMType]newSCMProviderFunc)
}
// RegisterProvider registers SCM providers.
func RegisterProvider(scmType api.SCMType, pFunc newSCMProviderFunc) error {
if _, ok := scmProviders[scmType]; ok {
return fmt.Errorf("SCM provider %s already exists.", scmType)
}
scmProviders[scmType] = pFunc
return nil
}
// SCMProvider represents the interface of SCM provider.
type SCMProvider interface {
GetToken() (string, error)
ListRepos() ([]api.Repository, error)
ListBranches(repo string) ([]string, error)
ListTags(repo string) ([]string, error)
GetTemplateType(repo string) (string, error)
CheckToken() bool
NewTagFromLatest(tagName, description, commitID, url string) error
CreateWebHook(repoURL string, webHook *WebHook) error
DeleteWebHook(repoURL string, webHookUrl string) error
CreateStatus(recordStatus api.Status, targetURL, repoURL, commitSha string) error
}
// WebHook represents the params for SCM webhook.
type WebHook struct {
Events []EventType
Url string
}
type EventType string
const (
PullRequestEventType EventType = "PullRequest"
PullRequestCommentEventType EventType = "PullRequestComment"
PushEventType EventType = "Push"
TagReleaseEventType EventType = "TagRelease"
)
// GetSCMProvider gets the SCM provider by the type.
func GetSCMProvider(scm *api.SCMConfig) (SCMProvider, error) {
if scm == nil {
err := fmt.Errorf("SCM config is nil")
log.Error(err)
return nil, err
}
scmType := scm.Type
pFunc, ok := scmProviders[scmType]
if !ok {
return nil, httperror.ErrorUnsupported.Error("SCM type", scmType)
}
return pFunc(scm)
}
// GenerateSCMToken generates the SCM token according to the config.
// Make sure the type, server of the SCM is provided. If the SCM is Github, the username is required.
// If the access token is provided, it should be checked whether has authority of repos.
// Generate new token only when the username and password are provided at the same time.
func GenerateSCMToken(config *api.SCMConfig) error {
if config == nil {
return httperror.ErrorContentNotFound.Error("SCM config")
}
if config.AuthType != api.Password && config.AuthType != api.Token {
return httperror.ErrorUnsupported.Error("SCM authType", config.AuthType)
}
// Trim suffix '/' of Gitlab server to ensure that the token can work, otherwise there will be 401 error.
config.Server = strings.TrimSuffix(config.Server, "/")
scmType := config.Type
provider, err := GetSCMProvider(config)
if err != nil {
return err
}
var generatedToken string
switch scmType {
case api.Github:
// Github username is required.
if len(config.Username) == 0 {
return httperror.ErrorContentNotFound.Error("Github username")
}
// If Github password is provided, generate the new token.
if len(config.Password) != 0 {
generatedToken, err = provider.GetToken()
if err != nil {
log.Errorf("fail to get SCM token for user %s as %s", config.Username, err.Error())
return err
}
}
case api.Gitlab:
// If username and password is provided, generate the new token.
if len(config.Username) != 0 && len(config.Password) != 0 {
generatedToken, err = provider.GetToken()
if err != nil {
log.Errorf("fail to get SCM token for user %s as %s", config.Username, err.Error())
return err
}
}
case api.SVN:
generatedToken, _ = provider.GetToken()
default:
return httperror.ErrorUnsupported.Error("SCM type", scmType)
}
if generatedToken != "" {
config.Token = generatedToken
} else if !provider.CheckToken() {
return httperror.ErrorValidationFailed.Error("token", "unauthorized to repos")
}
// Cleanup the password for security.
config.Password = ""
return nil
}
func NewTagFromLatest(codeSource *api.CodeSource, scm *api.SCMConfig, tagName, description string) error {
commitID, err := wscm.GetCommitID(codeSource, "")
if err != nil {
return err
}
gitSource, err := api.GetGitSource(codeSource)
if err != nil {
return err
}
url := gitSource.Url
p, err := GetSCMProvider(scm)
if err != nil {
return err
}
err = p.NewTagFromLatest(tagName, description, commitID, url)
if err != nil {
return err
}
return nil
}