diff --git a/pkg/simple/client/devops/jenkins/pipeline.go b/pkg/simple/client/devops/jenkins/pipeline.go index de7e63c50..d859107e7 100644 --- a/pkg/simple/client/devops/jenkins/pipeline.go +++ b/pkg/simple/client/devops/jenkins/pipeline.go @@ -25,6 +25,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" "net/url" + "strconv" "strings" "time" ) @@ -169,6 +170,14 @@ func (p *Pipeline) GetPipelineRun() (*devops.PipelineRun, error) { } func (p *Pipeline) ListPipelineRuns() (*devops.PipelineRunList, error) { + // prefer to use listPipelineRunsByRemotePaging once the corresponding issues from BlueOcean fixed + return p.listPipelineRunsByLocalPaging() +} + +// listPipelineRunsByRemotePaging get the pipeline runs with pagination by remote (Jenkins BlueOcean plugin) +// get the pagination information from the server side is better than the local side, but the API has some issues +// see also https://github.com/kubesphere/kubesphere/issues/3507 +func (p *Pipeline) listPipelineRunsByRemotePaging() (*devops.PipelineRunList, error) { res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) if err != nil { klog.Error(err) @@ -190,13 +199,65 @@ func (p *Pipeline) ListPipelineRuns() (*devops.PipelineRunList, error) { return &pipelineRunList, err } -func (p *Pipeline) searchPipelineRunsCount() (int, error) { +// listPipelineRunsByLocalPaging should be a temporary solution +// see also https://github.com/kubesphere/kubesphere/issues/3507 +func (p *Pipeline) listPipelineRunsByLocalPaging() (runList *devops.PipelineRunList, err error) { + desiredStart, desiredLimit := p.parsePaging() + + var pageUrl *url.URL // get all Pipeline runs + if pageUrl, err = p.resetPaging(0, 10000); err != nil { + return + } + res, err := p.Jenkins.SendPureRequest(pageUrl.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + return nil, err + } + + runList = &devops.PipelineRunList{ + Items: make([]devops.PipelineRun, 0), + } + if err = json.Unmarshal(res, &runList.Items); err != nil { + klog.Error(err) + return nil, err + } + + // set the total count number + runList.Total = len(runList.Items) + + // keep the desired data/ + if desiredStart+1 >= runList.Total { + // beyond the boundary, return an empty + return + } + + endIndex := runList.Total + if desiredStart+desiredLimit < endIndex { + endIndex = desiredStart + desiredLimit + } + runList.Items = runList.Items[desiredStart:endIndex] + return +} + +// resetPaging reset the paging setting from request, support start, limit +func (p *Pipeline) resetPaging(start, limit int) (path *url.URL, err error) { query, _ := ParseJenkinsQuery(p.HttpParameters.Url.RawQuery) - query.Set("start", "0") - query.Set("limit", "1000") - query.Set("depth", "-1") + query.Set("start", strconv.Itoa(start)) + query.Set("limit", strconv.Itoa(limit)) p.HttpParameters.Url.RawQuery = query.Encode() - u, err := url.Parse(p.Path) + path, err = url.Parse(p.Path) + return +} + +func (p *Pipeline) parsePaging() (start, limit int) { + query, _ := ParseJenkinsQuery(p.HttpParameters.Url.RawQuery) + start, _ = strconv.Atoi(query.Get("start")) + limit, _ = strconv.Atoi(query.Get("limit")) + return +} + +func (p *Pipeline) searchPipelineRunsCount() (int, error) { + u, err := p.resetPaging(0, 1000) if err != nil { return 0, err } diff --git a/pkg/simple/client/devops/jenkins/pipeline_test.go b/pkg/simple/client/devops/jenkins/pipeline_test.go new file mode 100644 index 000000000..ca4bca6e3 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pipeline_test.go @@ -0,0 +1,106 @@ +package jenkins + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/url" + "strconv" + "testing" +) + +func TestResetPaging(t *testing.T) { + table := []struct { + path string + rawQuery string + start int + limit int + hasErr bool + message string + }{{ + start: 0, + limit: 10, + hasErr: false, + message: "without query, should no errors", + }, { + path: "/fake/path", + rawQuery: "?start=1&limit1", + start: 0, + limit: 10, + hasErr: false, + message: "without a query", + }, { + path: "/fake/path", + rawQuery: "?start=1&limit1", + start: 3, + limit: 13, + hasErr: false, + message: "without a query", + }} + + for index, item := range table { + pip := &Pipeline{ + Path: item.path, + HttpParameters: &devops.HttpParameters{ + Url: &url.URL{ + Path: item.path, + RawQuery: item.rawQuery, + }, + }, + } + + resultPath, err := pip.resetPaging(item.start, item.limit) + if item.hasErr { + assert.NotNil(t, err, printTestMessage(index, item.message)) + } else { + assert.Nil(t, err, printTestMessage(index, item.message)) + + assert.Equal(t, item.path, resultPath.Path, printTestMessage(index, item.message)) + assert.Equal(t, strconv.Itoa(item.start), pip.HttpParameters.Url.Query().Get("start"), + printTestMessage(index, item.message)) + assert.Equal(t, strconv.Itoa(item.limit), pip.HttpParameters.Url.Query().Get("limit"), + printTestMessage(index, item.message)) + } + } +} + +func TestParsePaging(t *testing.T) { + table := []struct { + targetUrl string + start int + limit int + message string + }{{ + targetUrl: "http://localhost?start=0&limit=0", + start: 0, + limit: 0, + message: "should be success", + }, { + targetUrl: "http://localhost?start=1&limit=10", + start: 1, + limit: 10, + message: "should be success", + }, { + targetUrl: "http://localhost?start=5&limit=55", + start: 5, + limit: 55, + message: "should be success", + }} + + for index, item := range table { + pipUrl, _ := url.Parse(item.targetUrl) + pip := &Pipeline{ + HttpParameters: &devops.HttpParameters{ + Url: pipUrl, + }, + } + resultStart, resultLimit := pip.parsePaging() + + assert.Equal(t, item.start, resultStart, printTestMessage(index, item.message)) + assert.Equal(t, item.limit, resultLimit, printTestMessage(index, item.message)) + } +} + +func printTestMessage(index int, message string) string { + return fmt.Sprintf("index: %d, message: %s", index, message) +}