Skip to content

Commit

Permalink
Add pagination to tm bot ui (#260)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Schrodi authored Apr 1, 2020
1 parent 9022135 commit ddbda3a
Show file tree
Hide file tree
Showing 13 changed files with 440 additions and 188 deletions.
6 changes: 6 additions & 0 deletions pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ const (

// TM Dashboard
DashboardExecutionGroupParameter = "runID"

// DashboardPaginationFrom is the name of the http parameter for the pagination from index.
DashboardPaginationFrom = "from"

// DashboardPaginationTo is the name of the http parameter for the pagination from index.
DashboardPaginationTo = "to"
)

var (
Expand Down
File renamed without changes.
File renamed without changes.
86 changes: 86 additions & 0 deletions pkg/tm-bot/ui/pages/pagination/pagination.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2020 Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file.
//
// 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 pagination

import (
"github.com/gardener/test-infra/pkg/common"
"net/url"
"sort"
"strconv"
)

type Interface interface {
sort.Interface
GetPaginatedList(from, to int) Interface
}

type Pages struct {
Pages []Page
Current int
ItemCount int
}

type Page struct {
From int
To int
}

const itemsPerPage = 50

// SliceFromValues slices the given list into the values gathered form the url values
func SliceFromValues(list Interface, values url.Values) (Interface, Pages) {
sort.Sort(list)
if list.Len() < itemsPerPage {
return list, Pages{Pages: []Page{}, Current: 0, ItemCount: list.Len()}
}

indexLength := list.Len() - 1

pages := Pages{Pages: []Page{}, Current: 0, ItemCount: list.Len()}
for i := 0; i < (pages.ItemCount / itemsPerPage); i++ {
from := i * itemsPerPage
to := from + itemsPerPage - 1
if to > indexLength {
to = indexLength
}
pages.Pages = append(pages.Pages, Page{
From: from,
To: to,
})
}

fromString, ok := values[common.DashboardPaginationFrom]
if !ok {
return list.GetPaginatedList(0, itemsPerPage), pages
}

toString, ok := values[common.DashboardPaginationTo]
if !ok {
return list.GetPaginatedList(0, itemsPerPage), pages
}

from, err := strconv.Atoi(fromString[0])
if err != nil {
return list.GetPaginatedList(0, itemsPerPage), pages
}
to, err := strconv.Atoi(toString[0])
if err != nil {
return list.GetPaginatedList(0, itemsPerPage), pages
}

pages.Current = from / itemsPerPage

return list.GetPaginatedList(from, to), pages
}
189 changes: 14 additions & 175 deletions pkg/tm-bot/ui/pages/Runs.go → pkg/tm-bot/ui/pages/runs.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ import (
argov1alpha1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
"github.com/gardener/test-infra/pkg/apis/testmachinery/v1beta1"
"github.com/gardener/test-infra/pkg/common"
metadata2 "github.com/gardener/test-infra/pkg/testmachinery/metadata"
tmetadata "github.com/gardener/test-infra/pkg/testmachinery/metadata"
"github.com/gardener/test-infra/pkg/testrunner"
"github.com/gardener/test-infra/pkg/tm-bot/ui/pages/pagination"
"github.com/gardener/test-infra/pkg/util"
"github.com/gardener/test-infra/pkg/util/output"
"github.com/gorilla/mux"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"net/http"
"sigs.k8s.io/controller-runtime/pkg/client"
"sort"
"strings"
"time"
)

Expand All @@ -49,26 +47,6 @@ type testrunItem struct {
GrafanaURL string
}

type detailedTestrunItem struct {
testrunItem
Steps testrunStepStatusItemList
RawStatus string
}

type testrunStepStatusItem struct {
step *v1beta1.StepStatus
Name string
Step string
Phase IconWithTooltip
StartTime string
Duration string
Location string

IsSystem bool

GrafanaURL string
}

type rungroupItem struct {
testruns []*v1beta1.Testrun
phase argov1alpha1.NodePhase
Expand All @@ -88,17 +66,16 @@ func NewTestrunsPage(p *Page) http.HandlerFunc {
defer ctx.Done()

var (
rgName string
hasRunGroup bool
listOpts = client.MatchingLabels(map[string]string{})
rgName string
listOpts = []client.ListOption{}
)
if rg, rgOk := r.URL.Query()[common.DashboardExecutionGroupParameter]; rgOk {
hasRunGroup = true
rgName = rg[0]
listOpts = client.MatchingLabels(map[string]string{common.LabelTestrunExecutionGroup: rgName})
listOpts = append(listOpts, client.MatchingLabels(map[string]string{common.LabelTestrunExecutionGroup: rgName}))
}

runs := &v1beta1.TestrunList{}
if err := p.runs.GetClient().List(ctx, runs, listOpts); err != nil {
if err := p.runs.GetClient().List(ctx, runs, listOpts...); err != nil {
p.log.Error(err, "unable to fetch testruns")
http.Redirect(w, r, "/404", http.StatusTemporaryRedirect)
return
Expand All @@ -115,7 +92,7 @@ func NewTestrunsPage(p *Page) http.HandlerFunc {
if rgName == "" {
runsList.Add(&testrun)
}
metadata := metadata2.FromTestrun(&tr)
metadata := tmetadata.FromTestrun(&tr)
startTime := ""
if tr.Status.StartTime != nil {
startTime = tr.Status.StartTime.Format(time.RFC822)
Expand Down Expand Up @@ -146,20 +123,17 @@ func NewTestrunsPage(p *Page) http.HandlerFunc {
}
}

paginatedTestruns, pages := pagination.SliceFromValues(testrunsList, r.URL.Query())
sort.Sort(testrunsList)
sort.Sort(runsList)
params := map[string]interface{}{
"rungroup": rgName,
"tests": testrunsList,
"rungroups": runsList,
"rungroup": rgName,
"tests": paginatedTestruns,
"pages": pages,
"rungroups": runsList,
"pagination": false,
}

// TODO: add pagination
if !hasRunGroup {
if len(testrunsList) > 50 {
params["tests"] = testrunsList[:50] // todo add pagination to not cut at 50 items
}
}
if len(runsList) > 6 {
params["rungroups"] = runsList[:6]
}
Expand All @@ -168,141 +142,6 @@ func NewTestrunsPage(p *Page) http.HandlerFunc {
}
}

func NewTestrunPage(p *Page) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
defer ctx.Done()

trName := client.ObjectKey{
Name: mux.Vars(r)["testrun"],
Namespace: mux.Vars(r)["namespace"],
}

tr := &v1beta1.Testrun{}
if err := p.runs.GetClient().Get(ctx, trName, tr); err != nil {
http.Redirect(w, r, "/404", http.StatusTemporaryRedirect)
return
}

argoHostURL, _ := testrunner.GetArgoHost(p.runs.GetClient())
grafanaHostURL, _ := testrunner.GetGrafanaHost(p.runs.GetClient())
metadata := metadata2.FromTestrun(tr)
startTime := ""
if tr.Status.StartTime != nil {
startTime = tr.Status.StartTime.Format(time.RFC822)
}
d := time.Duration(tr.Status.Duration) * time.Second
if tr.Status.Duration == 0 && !tr.Status.StartTime.IsZero() {
d = time.Now().Sub(tr.Status.StartTime.Time)
d = d / time.Second * time.Second // remove unnecessary milliseconds
}

statusTable := &strings.Builder{}
if len(tr.Status.Steps) != 0 {
output.RenderStatusTable(statusTable, tr.Status.Steps)
}

item := detailedTestrunItem{
testrunItem: testrunItem{
testrun: tr,
ID: tr.GetName(),
Namespace: tr.GetNamespace(),
RunID: tr.GetLabels()[common.LabelTestrunExecutionGroup],
Phase: PhaseIcon(tr.Status.Phase),
StartTime: startTime,
Duration: d.String(),
Progress: util.TestrunProgress(tr),
Dimension: metadata.GetDimensionFromMetadata("/"),
},
Steps: make(testrunStepStatusItemList, len(tr.Status.Steps)),
RawStatus: statusTable.String(),
}
if argoHostURL != "" {
item.ArgoURL = testrunner.GetArgoURLFromHost(argoHostURL, tr)
}
if grafanaHostURL != "" {
item.GrafanaURL = testrunner.GetGrafanaURLFromHostForWorkflow(grafanaHostURL, tr)
}

for i, step := range tr.Status.Steps {
startTime := ""
if step.StartTime != nil {
startTime = step.StartTime.Format(time.RFC822)
}
d := time.Duration(step.Duration) * time.Second
if step.Duration == 0 && !step.StartTime.IsZero() {
d = time.Now().Sub(step.StartTime.Time)
d = d / time.Second * time.Second // remove unnecessary milliseconds
}
item.Steps[i] = testrunStepStatusItem{
step: step,
Name: step.TestDefinition.Name,
Step: step.Position.Step,
Phase: PhaseIcon(step.Phase),
StartTime: startTime,
Duration: d.String(),
Location: fmt.Sprintf("%s:%s", step.TestDefinition.Location.Repo, step.TestDefinition.Location.Revision),
IsSystem: util.IsSystemStep(step),
}
if grafanaHostURL != "" {
item.Steps[i].GrafanaURL = testrunner.GetGrafanaURLFromHostForStep(grafanaHostURL, tr, step)
}
}

sort.Sort(item.Steps)

p.handleSimplePage("testrun.html", item)(w, r)
}
}

type testrunItemList []testrunItem

func (l testrunItemList) Len() int { return len(l) }
func (l testrunItemList) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
func (l testrunItemList) Less(a, b int) bool {
aTr := l[a].testrun
bTr := l[b].testrun
if aTr.Status.Phase != bTr.Status.Phase {
if aTr.Status.Phase == v1beta1.PhaseStatusRunning {
return true
}
if bTr.Status.Phase == v1beta1.PhaseStatusRunning {
return false
}
}
if aTr.Status.StartTime == nil || bTr.Status.StartTime == nil {
return true
}

return bTr.Status.StartTime.Before(aTr.Status.StartTime)
}

type testrunStepStatusItemList []testrunStepStatusItem

func (l testrunStepStatusItemList) Len() int { return len(l) }
func (l testrunStepStatusItemList) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
func (l testrunStepStatusItemList) Less(a, b int) bool {
if l[a].step.Phase != l[b].step.Phase {
if l[a].step.Phase == v1beta1.PhaseStatusRunning {
return true
}
if l[b].step.Phase == v1beta1.PhaseStatusRunning {
return false
}

if l[a].step.Phase == v1beta1.PhaseStatusInit {
return false
}
if l[b].step.Phase == v1beta1.PhaseStatusInit {
return true
}
}
if l[a].step.StartTime == nil || l[b].step.StartTime == nil {
return true
}
return l[a].step.StartTime.Before(l[b].step.StartTime)
}

type rungroupItemList []rungroupItem

func (l rungroupItemList) Len() int { return len(l) }
Expand Down
File renamed without changes.
Loading

0 comments on commit ddbda3a

Please sign in to comment.