37 Star 403 Fork 75

GVPrancher/rancher

Create your Gitee Account
Explore and code with more than 12 million developers,Free private repositories !:)
Sign up
Clone or Download
pipeline.go 15.53 KB
Copy Edit Raw Blame History
Caleb Bron authored 2020-03-18 12:34 . Add action auth checks
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
package pipeline
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/pkg/errors"
"github.com/rancher/norman/api/access"
"github.com/rancher/norman/httperror"
"github.com/rancher/norman/types"
"github.com/rancher/rancher/pkg/pipeline/providers"
"github.com/rancher/rancher/pkg/pipeline/remote"
"github.com/rancher/rancher/pkg/pipeline/remote/model"
"github.com/rancher/rancher/pkg/pipeline/utils"
"github.com/rancher/rancher/pkg/rbac"
"github.com/rancher/rancher/pkg/ref"
v3 "github.com/rancher/types/apis/project.cattle.io/v3"
client "github.com/rancher/types/client/project/v3"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/labels"
)
const (
actionRun = "run"
actionPushConfig = "pushconfig"
linkConfigs = "configs"
linkYaml = "yaml"
linkBranches = "branches"
queryBranch = "branch"
queryConfigs = "configs"
)
type Handler struct {
PipelineLister v3.PipelineLister
PipelineExecutions v3.PipelineExecutionInterface
SourceCodeCredentialLister v3.SourceCodeCredentialLister
SourceCodeCredentials v3.SourceCodeCredentialInterface
}
func Formatter(apiContext *types.APIContext, resource *types.RawResource) {
if canCreatePipelineExecutionFromPipeline(apiContext, resource) {
resource.AddAction(apiContext, actionRun)
}
if canUpdatePipeline(apiContext, resource) {
resource.AddAction(apiContext, actionPushConfig)
}
resource.Links[linkConfigs] = apiContext.URLBuilder.Link(linkConfigs, resource)
resource.Links[linkYaml] = apiContext.URLBuilder.Link(linkYaml, resource)
resource.Links[linkBranches] = apiContext.URLBuilder.Link(linkBranches, resource)
}
func (h *Handler) LinkHandler(apiContext *types.APIContext, next types.RequestHandler) error {
if apiContext.Link == linkYaml {
if apiContext.Method == http.MethodPut {
return h.updatePipelineConfigYaml(apiContext)
}
return h.getPipelineConfigYAML(apiContext)
} else if apiContext.Link == linkConfigs {
return h.getPipelineConfigJSON(apiContext)
} else if apiContext.Link == linkBranches {
return h.getValidBranches(apiContext)
}
return httperror.NewAPIError(httperror.NotFound, "Link not found")
}
func (h *Handler) ActionHandler(actionName string, action *types.Action, apiContext *types.APIContext) error {
switch actionName {
case actionRun:
if !canCreatePipelineExecutionFromPipeline(apiContext, nil) {
return httperror.NewAPIError(httperror.NotFound, "not found")
}
return h.run(apiContext)
case actionPushConfig:
if !canUpdatePipeline(apiContext, nil) {
return httperror.NewAPIError(httperror.NotFound, "not found")
}
return h.pushConfig(apiContext)
}
return httperror.NewAPIError(httperror.InvalidAction, "unsupported action")
}
func (h *Handler) run(apiContext *types.APIContext) error {
ns, name := ref.Parse(apiContext.ID)
pipeline, err := h.PipelineLister.Get(ns, name)
if err != nil {
return err
}
runPipelineInput := v3.RunPipelineInput{}
requestBytes, err := ioutil.ReadAll(apiContext.Request.Body)
if err != nil {
return err
}
if string(requestBytes) != "" {
if err := json.Unmarshal(requestBytes, &runPipelineInput); err != nil {
return err
}
}
branch := runPipelineInput.Branch
if branch == "" {
return httperror.NewAPIError(httperror.InvalidBodyContent, "Error branch is not specified for the pipeline to run")
}
userName := apiContext.Request.Header.Get("Impersonate-User")
pipelineConfig, err := providers.GetPipelineConfigByBranch(h.SourceCodeCredentials, h.SourceCodeCredentialLister, pipeline, branch)
if err != nil {
return err
}
if pipelineConfig == nil {
return fmt.Errorf("find no pipeline config to run in the branch")
}
info, err := h.getBuildInfoByBranch(pipeline, branch)
if err != nil {
return err
}
info.TriggerType = utils.TriggerTypeUser
info.TriggerUserName = userName
execution, err := utils.GenerateExecution(h.PipelineExecutions, pipeline, pipelineConfig, info)
if err != nil {
return err
}
if execution == nil {
return errors.New("condition is not match, no build is triggered")
}
data := map[string]interface{}{}
if err := access.ByID(apiContext, apiContext.Version, client.PipelineExecutionType, ref.Ref(execution), &data); err != nil {
return err
}
apiContext.WriteResponse(http.StatusOK, data)
return err
}
func (h *Handler) pushConfig(apiContext *types.APIContext) error {
ns, name := ref.Parse(apiContext.ID)
pipeline, err := h.PipelineLister.Get(ns, name)
if err != nil {
return err
}
pushConfigInput := v3.PushPipelineConfigInput{}
requestBytes, err := ioutil.ReadAll(apiContext.Request.Body)
if err != nil {
return err
}
if string(requestBytes) != "" {
if err := json.Unmarshal(requestBytes, &pushConfigInput); err != nil {
return err
}
}
//use current user's auth to do the push
userName := apiContext.Request.Header.Get("Impersonate-User")
creds, err := h.SourceCodeCredentialLister.List(userName, labels.Everything())
if err != nil {
return err
}
accessToken := ""
sourceCodeType := model.GithubType
var credential *v3.SourceCodeCredential
for _, cred := range creds {
if cred.Spec.ProjectName == pipeline.Spec.ProjectName && !cred.Status.Logout {
accessToken = cred.Spec.AccessToken
sourceCodeType = cred.Spec.SourceCodeType
credential = cred
break
}
}
_, projID := ref.Parse(pipeline.Spec.ProjectName)
scpConfig, err := providers.GetSourceCodeProviderConfig(sourceCodeType, projID)
if err != nil {
return err
}
remote, err := remote.New(scpConfig)
if err != nil {
return err
}
accessToken, err = utils.EnsureAccessToken(h.SourceCodeCredentials, remote, credential)
if err != nil {
return err
}
for branch, config := range pushConfigInput.Configs {
content, err := utils.PipelineConfigToYaml(&config)
if err != nil {
return err
}
if err := remote.SetPipelineFileInRepo(pipeline.Spec.RepositoryURL, branch, accessToken, content); err != nil {
if apierr, ok := err.(*httperror.APIError); ok && apierr.Code.Status == http.StatusNotFound {
//github returns 404 for unauth request to prevent leakage of private repos
return httperror.NewAPIError(httperror.Unauthorized, "current git account is unauthorized for the action")
}
return err
}
}
data := map[string]interface{}{}
if err := access.ByID(apiContext, apiContext.Version, apiContext.Type, apiContext.ID, &data); err != nil {
return err
}
apiContext.WriteResponse(http.StatusOK, data)
return nil
}
func (h *Handler) getBuildInfoByBranch(pipeline *v3.Pipeline, branch string) (*model.BuildInfo, error) {
credentialName := pipeline.Spec.SourceCodeCredentialName
repoURL := pipeline.Spec.RepositoryURL
accessToken := ""
sourceCodeType := model.GithubType
var scpConfig interface{}
var credential *v3.SourceCodeCredential
var err error
if credentialName != "" {
ns, name := ref.Parse(credentialName)
credential, err = h.SourceCodeCredentialLister.Get(ns, name)
if err != nil {
return nil, err
}
sourceCodeType = credential.Spec.SourceCodeType
accessToken = credential.Spec.AccessToken
_, projID := ref.Parse(pipeline.Spec.ProjectName)
scpConfig, err = providers.GetSourceCodeProviderConfig(sourceCodeType, projID)
if err != nil {
return nil, err
}
}
remote, err := remote.New(scpConfig)
if err != nil {
return nil, err
}
accessToken, err = utils.EnsureAccessToken(h.SourceCodeCredentials, remote, credential)
if err != nil {
return nil, err
}
info, err := remote.GetHeadInfo(repoURL, branch, accessToken)
if err != nil {
return nil, err
}
return info, nil
}
func (h *Handler) getValidBranches(apiContext *types.APIContext) error {
ns, name := ref.Parse(apiContext.ID)
pipeline, err := h.PipelineLister.Get(ns, name)
if err != nil {
return err
}
accessKey := ""
sourceCodeType := model.GithubType
var scpConfig interface{}
var cred *v3.SourceCodeCredential
if pipeline.Spec.SourceCodeCredentialName != "" {
ns, name = ref.Parse(pipeline.Spec.SourceCodeCredentialName)
cred, err = h.SourceCodeCredentialLister.Get(ns, name)
if err != nil {
return err
}
accessKey = cred.Spec.AccessToken
sourceCodeType = cred.Spec.SourceCodeType
_, projID := ref.Parse(pipeline.Spec.ProjectName)
scpConfig, err = providers.GetSourceCodeProviderConfig(sourceCodeType, projID)
if err != nil {
return err
}
}
remote, err := remote.New(scpConfig)
if err != nil {
return err
}
accessKey, err = utils.EnsureAccessToken(h.SourceCodeCredentials, remote, cred)
if err != nil {
return err
}
validBranches := map[string]bool{}
branches, err := remote.GetBranches(pipeline.Spec.RepositoryURL, accessKey)
if err != nil {
return err
}
for _, b := range branches {
content, err := remote.GetPipelineFileInRepo(pipeline.Spec.RepositoryURL, b, accessKey)
if err != nil {
return err
}
logrus.Debugf("get content in branch %s:%v", b, string(content))
if content != nil {
validBranches[b] = true
}
}
result := []string{}
for b := range validBranches {
result = append(result, b)
}
bytes, err := json.Marshal(result)
if err != nil {
return err
}
apiContext.Response.Write(bytes)
return nil
}
func (h *Handler) getPipelineConfigJSON(apiContext *types.APIContext) error {
ns, name := ref.Parse(apiContext.ID)
pipeline, err := h.PipelineLister.Get(ns, name)
if err != nil {
return err
}
branch := apiContext.Request.URL.Query().Get(queryBranch)
m, err := h.getPipelineConfigs(pipeline, branch)
if err != nil {
return err
}
bytes, err := json.Marshal(m)
if err != nil {
return err
}
apiContext.Response.Write(bytes)
return nil
}
func (h *Handler) getPipelineConfigYAML(apiContext *types.APIContext) error {
yamlMap := map[string]interface{}{}
m := map[string]*v3.PipelineConfig{}
branch := apiContext.Request.URL.Query().Get(queryBranch)
configs := apiContext.Request.URL.Query().Get(queryConfigs)
if configs != "" {
err := json.Unmarshal([]byte(configs), &m)
if err != nil {
return err
}
for b, config := range m {
if config == nil {
yamlMap[b] = nil
continue
}
content, err := utils.PipelineConfigToYaml(config)
if err != nil {
return err
}
yamlMap[b] = string(content)
}
} else {
ns, name := ref.Parse(apiContext.ID)
pipeline, err := h.PipelineLister.Get(ns, name)
if err != nil {
return err
}
m, err = h.getPipelineConfigs(pipeline, branch)
if err != nil {
return err
}
}
if branch != "" {
config := m[branch]
if config == nil {
return nil
}
content, err := utils.PipelineConfigToYaml(config)
if err != nil {
return err
}
apiContext.Response.Write(content)
return nil
}
for b, config := range m {
if config == nil {
yamlMap[b] = nil
continue
}
content, err := utils.PipelineConfigToYaml(config)
if err != nil {
return err
}
yamlMap[b] = string(content)
}
bytes, err := json.Marshal(yamlMap)
if err != nil {
return err
}
apiContext.Response.Write(bytes)
return nil
}
func (h *Handler) updatePipelineConfigYaml(apiContext *types.APIContext) error {
branch := apiContext.Request.URL.Query().Get(queryBranch)
if branch == "" {
return httperror.NewAPIError(httperror.InvalidOption, "Branch is not specified")
}
ns, name := ref.Parse(apiContext.ID)
pipeline, err := h.PipelineLister.Get(ns, name)
if err != nil {
return err
}
content, err := ioutil.ReadAll(apiContext.Request.Body)
if err != nil {
return err
}
//check yaml
config := &v3.PipelineConfig{}
if err := yaml.Unmarshal(content, config); err != nil {
return err
}
//use current user's auth to do the push
userName := apiContext.Request.Header.Get("Impersonate-User")
creds, err := h.SourceCodeCredentialLister.List(userName, labels.Everything())
if err != nil {
return err
}
accessToken := ""
sourceCodeType := model.GithubType
var credential *v3.SourceCodeCredential
for _, cred := range creds {
if cred.Spec.ProjectName == pipeline.Spec.ProjectName && !cred.Status.Logout {
accessToken = cred.Spec.AccessToken
sourceCodeType = cred.Spec.SourceCodeType
credential = cred
}
}
_, projID := ref.Parse(pipeline.Spec.ProjectName)
scpConfig, err := providers.GetSourceCodeProviderConfig(sourceCodeType, projID)
if err != nil {
return err
}
remote, err := remote.New(scpConfig)
if err != nil {
return err
}
accessToken, err = utils.EnsureAccessToken(h.SourceCodeCredentials, remote, credential)
if err != nil {
return err
}
if err := remote.SetPipelineFileInRepo(pipeline.Spec.RepositoryURL, branch, accessToken, content); err != nil {
if apierr, ok := err.(*httperror.APIError); ok && apierr.Code.Status == http.StatusNotFound {
//github returns 404 for unauth request to prevent leakage of private repos
return httperror.NewAPIError(httperror.Unauthorized, "current git account is unauthorized for the action")
}
return err
}
return nil
}
func (h *Handler) getPipelineConfigs(pipeline *v3.Pipeline, branch string) (map[string]*v3.PipelineConfig, error) {
accessToken := ""
sourceCodeType := model.GithubType
var scpConfig interface{}
var cred *v3.SourceCodeCredential
var err error
if pipeline.Spec.SourceCodeCredentialName != "" {
ns, name := ref.Parse(pipeline.Spec.SourceCodeCredentialName)
cred, err = h.SourceCodeCredentialLister.Get(ns, name)
if err != nil {
return nil, err
}
sourceCodeType = cred.Spec.SourceCodeType
accessToken = cred.Spec.AccessToken
_, projID := ref.Parse(pipeline.Spec.ProjectName)
scpConfig, err = providers.GetSourceCodeProviderConfig(sourceCodeType, projID)
if err != nil {
return nil, err
}
}
remote, err := remote.New(scpConfig)
if err != nil {
return nil, err
}
accessToken, err = utils.EnsureAccessToken(h.SourceCodeCredentials, remote, cred)
if err != nil {
return nil, err
}
m := map[string]*v3.PipelineConfig{}
if branch != "" {
content, err := remote.GetPipelineFileInRepo(pipeline.Spec.RepositoryURL, branch, accessToken)
if err != nil {
return nil, err
}
if content != nil {
spec, err := utils.PipelineConfigFromYaml(content)
if err != nil {
return nil, errors.Wrapf(err, "Error fetching pipeline config in Branch '%s'", branch)
}
m[branch] = spec
} else {
m[branch] = nil
}
} else {
branches, err := remote.GetBranches(pipeline.Spec.RepositoryURL, accessToken)
if err != nil {
return nil, err
}
for _, b := range branches {
content, err := remote.GetPipelineFileInRepo(pipeline.Spec.RepositoryURL, b, accessToken)
if err != nil {
return nil, err
}
if content != nil {
spec, err := utils.PipelineConfigFromYaml(content)
if err != nil {
return nil, errors.Wrapf(err, "Error fetching pipeline config in Branch '%s'", b)
}
m[b] = spec
} else {
m[b] = nil
}
}
}
return m, nil
}
func canUpdatePipeline(apiContext *types.APIContext, resource *types.RawResource) bool {
obj := rbac.ObjFromContext(apiContext, resource)
return apiContext.AccessControl.CanDo(
v3.PipelineGroupVersionKind.Group, v3.PipelineResource.Name, "update", apiContext, obj, apiContext.Schema,
) == nil
}
// This checks ability to execute pipeline from pipeline obj, not pipelineExecution obj
// Uses the project ID from the pipeline obj to check create permissions on executions in that NS
func canCreatePipelineExecutionFromPipeline(apiContext *types.APIContext, resource *types.RawResource) bool {
obj := make(map[string]interface{})
if resource != nil {
obj[rbac.NamespaceID], _ = ref.Parse(resource.ID)
} else {
obj[rbac.NamespaceID], _ = ref.Parse(apiContext.ID)
}
if val, ok := obj[rbac.NamespaceID]; !ok || val == "" {
return false
}
return apiContext.AccessControl.CanDo(
v3.PipelineExecutionGroupVersionKind.Group, v3.PipelineExecutionResource.Name, "create", apiContext, obj, apiContext.Schema,
) == nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/rancher/rancher.git
git@gitee.com:rancher/rancher.git
rancher
rancher
rancher
v2.2.13

Search