tektoncd / dashboard Goto Github PK
View Code? Open in Web Editor NEWA dashboard for Tekton!
License: Apache License 2.0
A dashboard for Tekton!
License: Apache License 2.0
The front end currently presents data from a number of APIs in most of its views. The logic required to correlate the pieces from different sources is subtly different between e.g. Pipeline and PipelineRun vs Task and TaskRun. This leads to unwanted complexity, additional maintenance overhead, and increases the risk of bugs.
So far we have used a small number of top-level container components which are responsible for making API calls and manipulating the responses into a form suitable for consumption by the presentation components. This approach will not scale long-term, especially as the data needs to be combined / presented in new and subtly different ways for other features / views.
Introducing a consistent state management layer which normalises the data received from the APIs and is isolated from the presentational layer will allow for greater flexibility, improved consistency, and easier testing.
Plan:
redux
(with react-redux
+ redux-thunk
for async actions) as a single source of truth
react-redux
's connect
, and provide relevant view of state as props to pure presentational componentsnormalizr
to ensure consistent and efficient storage of the data according to a simple schemamapDispatchToProps
to bind to store)reselect
or similar) to retrieve content from the store, containers/components shouldn't know the shape of the store
mapStateToProps
to provide appropriate inputs to the containersWe have a REST API that supports the manual creation of pipelineruns.
The parameters that a user must provide will be specific to the pipeline they're creating a run for - luckily they're all defined in the pipeline.
The UI will need to obtain and parse the desired pipeline before populating the fields that need user entry.
Extend the dashboard so that a user can specify the Git coordinates of new Pipeline, Task and other definitions that should be installed into the target namespace. We can use the API delivered under #24 to run a simple git clone / kubectl apply
pipeline to achieve this.
In this first instance, any git credentials must be set up by the user separately. We can add in credential support once we've fleshed that out.
We think we have a test bug here.
https://github.com/tektoncd/dashboard/pull/48/files#diff-d5fa729f55b16ff94238fa6931ff4724R82
Background
@jessm12 and I are adding PipelineRun creation code and Jess noticed when adding a new POST endpoint, that it's tested and we don't have a fake PipelineRun or fake Pipeline. In noticing this, she's picked up the Put204 method has a double post
check in there instead of puts
.
This means that when we run the unit tests as they are now on master, the existing PUT method for updating a PipelineRun (changing its status) is untested (because we don't have a fake PipelineRun or fake Pipeline).
Code
We think the problematic code is here:
func TestPut204(t *testing.T) {
t.Log("Checking 204 for PUT Routes")
// Creating resources first, then update
var resourceLocations []string
postFunc := func(t *testing.T, request *http.Request, response *http.Response) {
contentLocation, ok := response.Header["Content-Location"]
if !ok {
t.Errorf("Content-Location header not provided in %s method for resource type: %s",request.Method,getResourceType(request.URL.Path, request.Method))
} else {
// "Content-Location" header is only set with single value
resourceLocations = append(resourceLocations,contentLocation[0])
}
}
makeRequests(t,methodMap[http.MethodPost],http.MethodPost,postFunc) << should be .MethodPut
The unit tests give us:
--- PASS: TestPut204 (0.00s)
routes_test.go:70: Checking 204 for PUT Routes
routes_test.go:95: POST method: /v1/namespaces/fake/credential
routes_test.go:103: Response from server: &{201 Created 201 HTTP/1.1 1 1 map[Content-Length:[0] Content-Location:[/v1/namespaces/fake/credential/a309a104-3134-43d7-a142-9487fe86390b] Date:[Wed, 17 Apr 2019 09:25:38 GMT]] {} 0 [] false false map[] 0xc000480200 <nil>}
routes_test.go:95: PUT method: /v1/namespaces/fake/credential/a309a104-3134-43d7-a142-9487fe86390b
routes_test.go:103: Response from server: &{204 No Content 204 HTTP/1.1 1 1 map[Date:[Wed, 17 Apr 2019 09:25:38 GMT]] {} 0 [] false false map[] 0xc000558100 <nil>}
which isn't picking up the PipelineRun PUT API.
When we make it use PUTS we get:
--- FAIL: TestPut204 (0.00s)
routes_test.go:68: Checking 204 for PUT Routes
routes_test.go:93: PUT method: /v1/namespaces/fake/pipelinerun/{name}
routes_test.go:164: Fake template does not exist for crdType: pipelinerun
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x6e3a5f]
Under install/tekton-dashboard-deployment.yaml
, the deployment should have a label that allows for easier manipulation of it, such as getting and deleting the deployment
https://nodesecurity.io/advisories/755 currently in the web project
Credential REST API provides CURD operation for the secrets. The secrets must be patched into the service account used by the PipelineRun or TaskRun. It must be done manually now. This issue enhances the credential REST API to patch the service account automatically when the secret is created or deleted.
When patching secrets onto the service account, we might want to think about the fact that the user should be able to choose/specify a service account.
One reason why we might want to do this and move away from an environment variable, is because I think that you will restrict access to repositories.... this might need to be validated, but I believe you cannot have multiple secrets on a service account where the annotation on the secrets are for the same git server. This would mean you would need to use an account that can access all repos you might be interested in using from the git server.
Might it not be the case that for security reasons, different users might have to use different service accounts patched with their own secrets/credentials?
Under this issue add a REST API endpoint that will take parameters and create a new PipelineRun
for a specified Pipeline
When creating a new PipelineRun
the information that will need to be provided to the POST method is as follows:
Pipeline
to use for that PipelineRun
PipelineResources
for a PipelineRun
, if requiredPipelineRun
The request will be namespaced and this is the namespace that the PipelineRun will occur in
There are a number of test files for the go code, but no automation kicking off these tests.
Automated testing of the code is a pretty fundamental requirement.... this may mean working out how to integrate with prow which much of tekton seems to use already?
Currently if your installation is not in default you must edit index.js:
export function getAPI(type, id = '', namespace = 'default') {
and change 'default' to the target namespace. That's not great! Since the dashboard is expected to be deployed to the tekton-pipelines namespace it should be able to manage pipelines installed to any valid and accessible namespace on the cluster. At the very least, the user should be able to select the current namespace from a pull-down list of valid options.
Currently at the getting started section just says build image and run it.
But if you visit the localhost:9097 url (port-forwarding) you cannot see anything.
You need to run npm install & npm start to access web ui locally
Expose tasks as a top level construct with navigation to tasks and task runs.
New root navigation
Tasks view. This show all tasks.
Task Runs for the task selected in the previous navigation with no selected task run.
Selected task no selected step. Header updates with task run name and task run time for the given selected task
Task and step selection. Similar to pipeline run navigation
Notification where the selected task has never run.
@skaegi for review on the above. Alan and Myself iterated on it Thursday and Friday and settled on this layout. It Does add some of the navigation outlined in the design tickets.
We should have a DEVELOPMENT.md document that's likely going to share many steps from here but with any known limitations documented, and an easy to use development cycle for anyone to follow.
Currently we've not tested this using GKE and we don't yet support ko (but these would be awesome to have!) and so any such gotchas should be made clear.
The front-end and back-end handling of TaskRun logs are not currently in sync and need to be aligned to ensure the new combined project works as expected. The front-end Log
container and presentational components should be updated to handle the changed taskrunlog
response format returned by the API.
Create the proposed user flow for a scalable UI that will house Tekton capabilities. First address pipeline view with monitoring (dashboard). Design should be scalable to include eventing etc. moving forward.
Using the IBM DL, apply visual design to the experience outlined above.
Consider this look and feel may eventually need to wrap into the overall design of ICAP.
dibbles fixed this, we've got assigning problems atm
Run the Go unit tests in our Dockerfile several times in a row, sometimes you observe:
DeletionsRecorded not equal to 10, recorded only 9--- FAIL: TestPipelineRunWebsocket (17.00s)
websocket_test.go:75: Enter TestPipelineRunWebsocket...
websocket_test.go:239: DeletionsRecorded not equal to 10, recorded only 9
FAIL
exit status 1
FAIL github.com/tektoncd/dashboard/pkg/endpoints 24.096s
The command '/bin/sh -c CGO_ENABLED=0 NAMESPACE=default GOOS=linux go test -v' returned a non-zero code: 1
I ran this in a loop with the following straight from a stackoverflow Bash example.
I saw the failure twice in 18 failures, then stopped my script to make said issue.
i=0
output_file=tests.txt
rm -f $output_file
touch $output_file
while [ $i -lt 20 ] ; do
echo "Running test $i"
docker build -t foo -f Dockerfile_test . >> $output_file.txt
i=$[$i+1]
done
First seen here. @dibbles thinks this may be due to incrementing the counter variables in a non-atomic way and as such it's a test problem.
Interesting code in question: https://github.com/tektoncd/dashboard/blob/master/pkg/endpoints/websocket_test.go#L126 and the vars are declared:
// Counters for pipelineruns websocket test
var CreationsRecorded = 0
var UpdatesRecorded = 0
var DeletionsRecorded = 0
It would be great if the dashboard offered an extension mechanism. 'Extensions' could be packaged behind Kube services, with labels of the form,
label: tekton-dashboard-extension: true
label: tekton-dashboard-endpoints: greatThings
The extension should then be able to supply panelling into a react component on the dashboard - perhaps a new tab? - whose .js code would be able to interact with the extension via the /greatThings proxied endpoint. Extensions could support managing web hooks, log archiving, or integrations with other third party product.
At the moment if the front end app is deployed to for example /tekton-dashboard
it will fail to load parts of the UI. Similarly this means it can't be accessed using kube proxy at its context http://localhost:8001/api/v1/namespaces/tekton-pipelines/services/https:tekton-dashboard:/proxy/
kubectl apply -f https://raw.githubusercontent.com/tektoncd/dashboard/...
error: unable to read URL "https://raw.githubusercontent.com/tektoncd/dashboard/...", server reported 400 Bad Request, status code=400
Following install instructions in readme for master (at commit 170fa38) ..... after portforwarding, we just get a 404 in the browser.
Now that we have DEVELOPMENT.md we should document our APIs. We'll want return codes, paths, any errors that can happen, and bonus points for any json snippets!
At the moment the dashboard will only show resources in the default namespace.
This effort is to track listing namespaces and selecting a namespace for the pipeline and task runs views
API GET /v1/namespaces
See #26 - tried to approve @jessm12's PR, did so with approve
, details from Prow gives:
Details
Needs approval from an approver in each of these files:
OWNERS
We don't have such a file at https://github.com/tektoncd/dashboard/blob/master/OWNERS
Here's the one for tektoncd/pipeline though: https://github.com/tektoncd/pipeline/blob/master/OWNERS.
While adding #27 @vdemeester asked a question in a review comment: Out of curiosity, what doesn't work with ko ?
, and I've honestly not tried using ko
before with this project.
Prove we can use ko
to build and push and update DEVELOPMENT.md if so.
For tektoncd/pipeline I see lots of kodata folders (e.g. here).
It would be very cool to recommend doing a ko apply
and it'll just build and deploy our containers; at the least for now we can make sure it builds and deploys the backend using what's at install
.
Useful resources:
We've had success building and pushing knative repositories and tektoncd/pipeline both locally and to public Dockerhub, including updating the readme for tektoncd/pipeline to use a local registry. It's likely we can be more inline with what's done for tektoncd/pipeline and if so let's advocate that.
To avoid any surprises we should include a table of known good versions into our main readme, right at the top.
I've tested that we work with Tekton pipelines 0.2, I'd be interested to see if we work with master currently and if Tekton 0.1.
It would be very cool to have a compatibility table that's automatically updated, or we have public builds/tests available to all that just makes sure our code builds against master, so we know when something is broken. We should also look into having an equivalent to https://github.com/tektoncd/pipeline/blob/master/api_compatibility_policy.md.
For anyone who wants to work on this above point in particular, I do have a bunch of scripts I'm happy to share that iterates over each version of Tekton pipelines we care for, and any branches/versions of this dashboard, and knative for any extensions we want to make, and simply runs the Go tests after installing each component. I know a colleague is working on a real "app test" as well that would be awesome to use as the source for this compatibility table eventually.
Here is a first pass at the information architecture for the dashboard. Please provide feedback.
Tekton
Namespace
Extension Category 1 [from Service metadata] Knative Eventing
Extension Category 2 [from Service metadata] Native worker agent
For resources in the cluster like credentials that are tagged as associated with Tekton should be surfaced in the UI. Design coming...
This would allow us to provide richer documentation in a standard format, as well as interactive examples that would ease development.
Open API spec: https://swagger.io/specification/
Swagger UI example: https://petstore.swagger.io/
Something like:
openapi: 3.0.0
info:
title: Tekton Dashboard
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.0.1
servers:
- url: '{protocol}://{host}:{port}/v1/namespaces/{namespace}'
description: Optional server description, e.g. Main (production) server
variables:
protocol:
enum:
- http
- https
default: http
host:
default: localhost
port:
default: 9097
namespace:
default: tekton-pipelines
description: namespace where the Tekton resources (Pipelines, Tasks, etc.) are installed
paths:
/pipeline/:
get:
summary: Returns a list of Pipelines
description: Optional extended description in CommonMark or HTML.
parameters:
- in: path
name: namespace
schema:
type: string
example: tekton-pipelines
required: true
description: namespace in which the Pipelines are installed
responses:
'200':
description: A JSON array of Pipelines
content:
application/json:
schema:
type: array
items:
type: string
/pipeline/{name}:
get:
summary: Returns a sinlge Pipeline
description: Optional extended description in CommonMark or HTML.
parameters:
- in: path
name: name
schema:
type: string
example: demo-pipeline
required: true
description: name of the Pipeline to retrieve
responses:
'200':
description: A JSON object representing the requested Pipeline
content:
application/json:
schema:
type: object
Does documentation exist on how to deploy/install this on openshift?
In our package.json there's a lot that looks to me like dev dependencies (babel, webpack, eslint etc) - so this issue is to take a look at our package.json and see what can be classified as a dev dependency versus a regular one.
As a useful ref: https://github.com/CloudNativeJS/mern-workshop/blob/master/frontend/package.json
I was looking at the dashboard and I am wondering If there is a need for REST endpoints on the dashboard backend? Kubernetes already has an API server and tekton does everything as CRDs, wouldn't it be easier and more portable to develop the UI against kubernetes API server and tekton CRDs? What is the added value of the new REST endpoints?
The purpose of this issue is to provide an endpoint that creates a PipelineRun for any existing Pipeline, and to discuss how to go about implementing this endpoint.
The Pull Request for issue #24 is a good start to creating PipelineRuns. But it was limited to creating specific PipelineRuns for the Pipeline templates within our templates repository
https://github.com/pipeline-hotel/example-pipelines (here's a reference to the comment: #33 (comment))
Moving forward, the user should be able to start PipelineRuns for any Pipeline definition they have created. To do this, they must be able to specify the following (according to the Tekton docs https://github.com/tektoncd/pipeline/blob/master/docs/pipelineruns.md):
If we assume that the PipelineResources, Secrets, ServiceAccount, etc. that the PipelineRun uses already exist, then we do not need to do very much work to create the PipelineRun. All we have to do is convert the input JSON to the Go client PipelineRun (and PipelineRunSpec), then create the PipelineRun resource. The input JSON would look very similar to a K8s JSON definition of a PipelineRun. With this approach, the user would create all the resources required for the PipelineRun individually.
If we create PipelineRuns in this simplified manner, then it frees up our code to focus on verification. We can add checks to verify that all the required resources specified by the PipelineRun actually do exist and play nicely with each other. For example, we could verify that if a PipelineRun uses a private Git PipelineResource, then the ServiceAccount must have a Secret with the proper annotation for https://github.com
(and if it doesn't, then our UI can prompt the user to create & patch the secret).
This approach is different from that in issue #24, which creates all the resources necessary for the PipelineRun, and then creates the PipelineRun, knowing that everything is set up properly. While it works well for specific Pipelines, I think it will be hard to extend to work for any type of Pipeline.
I think the approach of creating resources individually with an emphasis on verification that I outlined above will work well for creating general PipelineRuns. What do you think @jessm12 @a-roberts ? Is there an alternate implementation that you have in mind? @skaegi What are the front-end concerns for this kind of PipelineRun endpoint?
We currently host our front-end in a webpack dev server and proxy to the back-end. We need to figure out how we want our url paths to the back-end to work and then host the results of a webpack build via a mechanism like https://github.com/emicklei/go-restful/blob/master/examples/restful-serve-static.go
This issue proposes a slight change to the context root handling mechanism for extensions introduced under #34.
The initial design mounts a context root /foo in an extension onto /foo at the main dashboard. This could become a problem: extensions could clash with each other, or the main dashboard. Instead we propose that:
tekton.dashboard.endpoints
is set to foo
then /v1/extension/extension-name/foo is routed to /foo at the extension.
character to separate them: e.g. if tekton.dashboard.endpoints: foo.bar
then /v1/extension/extension-name/foo is routed to /foo and /v1/extension/extension-name/bar is routed to /bar at the extension.There is some data race (caught by go test -race
) on ./pkg/endpoints
.
TestLogWebsocket
{"level":"info","msg":"Adding API for websocket"}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
==================
WARNING: DATA RACE
Read at 0x00c00047aee0 by goroutine 86:
github.com/tektoncd/dashboard/pkg/websocket.poll()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:73 +0x1ca
Previous write at 0x00c00047aee0 by goroutine 85:
github.com/tektoncd/dashboard/pkg/websocket.poll.func1()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:63 +0x46
github.com/tektoncd/dashboard/vendor/github.com/gorilla/websocket.(*Conn).advanceFrame()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/gorilla/websocket/conn.go:892 +0xac3
github.com/tektoncd/dashboard/vendor/github.com/gorilla/websocket.(*Conn).NextReader()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/gorilla/websocket/conn.go:947 +0x10a
github.com/tektoncd/dashboard/vendor/github.com/gorilla/websocket.(*Conn).ReadMessage()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/gorilla/websocket/conn.go:1028 +0x50
github.com/tektoncd/dashboard/pkg/websocket.readControl()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:51 +0x50
Goroutine 86 (running) created at:
github.com/tektoncd/dashboard/pkg/websocket.WriteOnlyWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:45 +0x99
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:37 +0xa0
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket-fm()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:32 +0x70
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:288 +0x108a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch-fm()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:203 +0x5f
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:1995 +0x51
net/http.(*ServeMux).ServeHTTP()
/usr/local/go/src/net/http/server.go:2375 +0x28a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).ServeHTTP()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:303 +0x6e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2774 +0xc4
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1878 +0x807
Goroutine 85 (running) created at:
github.com/tektoncd/dashboard/pkg/websocket.WriteOnlyWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:44 +0x77
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:37 +0xa0
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket-fm()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:32 +0x70
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:288 +0x108a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch-fm()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:203 +0x5f
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:1995 +0x51
net/http.(*ServeMux).ServeHTTP()
/usr/local/go/src/net/http/server.go:2375 +0x28a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).ServeHTTP()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:303 +0x6e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2774 +0xc4
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1878 +0x807
==================
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
==================
WARNING: DATA RACE
Write at 0x00c0000b11c0 by goroutine 88:
github.com/tektoncd/dashboard/pkg/broadcaster.(*Broadcaster).Unsubscribe()
/go/src/github.com/tektoncd/dashboard/pkg/broadcaster/broadcaster.go:123 +0xd7
github.com/tektoncd/dashboard/pkg/websocket.readControl()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:53 +0x121
Previous read at 0x00c0000b11c0 by goroutine 74:
github.com/tektoncd/dashboard/pkg/websocket.write()
/go/src/github.com/tektoncd/dashboard/pkg/broadcaster/broadcaster.go:51 +0x53
github.com/tektoncd/dashboard/pkg/websocket.WriteOnlyWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:46 +0xb1
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:37 +0xa0
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket-fm()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:32 +0x70
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:288 +0x108a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch-fm()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:203 +0x5f
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:1995 +0x51
net/http.(*ServeMux).ServeHTTP()
/usr/local/go/src/net/http/server.go:2375 +0x28a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).ServeHTTP()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:303 +0x6e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2774 +0xc4
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1878 +0x807
Goroutine 88 (running) created at:
github.com/tektoncd/dashboard/pkg/websocket.WriteOnlyWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/websocket/websocket.go:44 +0x77
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:37 +0xa0
github.com/tektoncd/dashboard/pkg/endpoints.Resource.establishPipelineLogsWebsocket-fm()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket.go:32 +0x70
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:288 +0x108a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).dispatch-fm()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:203 +0x5f
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:1995 +0x51
net/http.(*ServeMux).ServeHTTP()
/usr/local/go/src/net/http/server.go:2375 +0x28a
github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful.(*Container).ServeHTTP()
/go/src/github.com/tektoncd/dashboard/vendor/github.com/emicklei/go-restful/container.go:303 +0x6e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2774 +0xc4
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1878 +0x807
Goroutine 74 (running) created at:
net/http.(*Server).Serve()
/usr/local/go/src/net/http/server.go:2884 +0x4c4
net/http/httptest.(*Server).goServe.func1()
/usr/local/go/src/net/http/httptest/server.go:298 +0xac
==================
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
websocket_test.go:49: Enter TestLogWebsocket...
websocket_test.go:64: Waiting for clients to terminate...
websocket_test.go:74: Exit TestLogWebsocket
testing.go:809: race detected during execution of test
TestPipelineRunWebsocket
{"level":"info","msg":"Adding API for websocket"}
{"level":"debug","msg":"Into StartPipelineRunController"}
{"level":"info","msg":"PipelineRun Controller Started"}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
{"level":"debug","msg":"Upgrading connection to websocket..."}
Creating pipelinerun
{"level":"debug","msg":"In pipelineRunCreated"}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunCreated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:123456] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
Updating pipelinerun WebsocketPipelinerun
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunUpdated map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
Deleting pipelinerun: WebsocketPipelinerun
{"level":"debug","msg":"In pipelineRunDeleted"}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
{PipelineRunDeleted map[metadata:map[creationTimestamp:<nil> name:WebsocketPipelinerun namespace:ns1 resourceVersion:654321] spec:map[Status: params:<nil> pipelineRef:map[name:] resources:<nil> serviceAccount: trigger:map[type:]] status:map[conditions:<nil>]]}
==================
WARNING: DATA RACE
Read at 0x00000290c598 by goroutine 12:
github.com/tektoncd/dashboard/pkg/endpoints.TestPipelineRunWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket_test.go:115 +0x569
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
Previous write at 0x00000290c598 by goroutine 140:
github.com/tektoncd/dashboard/pkg/endpoints.clientWebsocket.func1()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket_test.go:172 +0x3ff
Goroutine 12 (running) created at:
testing.(*T).Run()
/usr/local/go/src/testing/testing.go:916 +0x699
testing.runTests.func1()
/usr/local/go/src/testing/testing.go:1157 +0xa8
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
testing.runTests()
/usr/local/go/src/testing/testing.go:1155 +0x523
testing.(*M).Run()
/usr/local/go/src/testing/testing.go:1072 +0x2eb
github.com/tektoncd/dashboard/pkg/endpoints.TestMain()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/routes_test.go:82 +0xfba
main.main()
_testmain.go:86 +0x222
Goroutine 140 (running) created at:
github.com/tektoncd/dashboard/pkg/endpoints.clientWebsocket()
/go/src/github.com/tektoncd/dashboard/pkg/endpoints/websocket_test.go:143 +0x31c
==================
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
{"level":"error","msg":"websocket connection to client lost: websocket: close 1005 (no status)"}
websocket_test.go:79: Enter TestPipelineRunWebsocket...
websocket_test.go:117: Waiting for clients to terminate...
websocket_test.go:127: Exit TestPipelineRunWebsocket
testing.go:809: race detected during execution of test
I disabled race detection on the CI for now (to bootstrap it), but this need to be fixed (so that we can re-enable race detections ๐ผ )
cc @a-roberts
The current dashboard is simple and that's ok for now but will not let us do the full range of navigation that experience says will be valuable for this sort of tool. This issue is to track some ideas on what me might do with the dashboard UI
The recent change to transparently handle non-root contexts in the URL is not fully compatible with the current webpack-dev-server configuration used in the dev environment. This can lead to API requests failing as they are not being rewritten correctly.
We're missing one, let's use the Tekton cat?
The pull request job does not run/report status for npm tests.
npm run test:ci
Similarly lint rules are not applied.
For example:
The back end should provide a websocket connection which publishes events to the front end.
Code exists today providing a dedicated websocket service for pipelineruns, and I have a test branch using that to dynamically update the PipelineRun list view in the front end.
The back end should provide a single websocket service through which all relevant events are sent, allowing the front end to maintain a single connection, rather than managing multiple parallel connections for various purposes.
Update https://github.com/tektoncd/dashboard/blob/master/DEVELOPMENT.md to include instructions for developing / testing / running the front end code locally (i.e. outside the Tekton cluster) until #14 is complete.
Some relevant content from previous front end README for reference:
[![Build Status](buildStatusBadgeURL)](repoURL) [![Component Storybook](https://img.shields.io/badge/component%20storybook-GHPages-blue.svg)](storybookURL) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat)](https://github.com/prettier/prettier) # dashboard React-based UI for Tekton pipelines ## Prerequisites [Node.js & npm](https://nodejs.org/). See `engines` in [pacakge.json](./package.json) for versions used. ## Getting started 1. Fork and clone this repository locally. ```bash git clone [email protected]:/dashboard.git cd dashboard ``` 1. Install dependencies. ```bash npm install ``` ## Development server Run `npm start` for a dev server. Navigate to `http://localhost:8000/` in your browser. The app will automatically hot-reload any changes to the source files, including CSS. If it is unable to hot-reload it will fallback to a full page refresh. ## Build Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. This will perform a production build of the static resources. It correctly bundles React in production mode and optimizes the build for the best performance. Filenames include hashes to facilitate long-term caching. ## Running unit tests Run `npm test` to execute the unit tests via [Jest](https://jestjs.io/) in interactive watch mode. This also generates a code coverage report by default. Coverage threshold is set to 90%, if it falls below the threshold the test script will fail. Tests are defined in `*.test.js` files alongside the code under test. ## Linting Run `npm run lint` to execute the linter (eslint + prettier). This will ensure code follows the conventions and standards used by the project. Run `npm run lint:fix` to automatically fix a number of types of problem including code style. This project also uses `lint-staged` and `husky` to automatically run `eslint` and `prettier` on changed files in a pre-commit hook. ## Storybook Run `npm run storybook` to start [storybook](https://storybook.js.org/) in development mode. Navigate to [`http://localhost:4000/`](http://localhost:4000/) in your browser. The app will automatically hot-reload any changes to the source files, including CSS. Stories are defined in `*.stories.js` files alongside their components. Run `npm run storybook:build` to build the static storybook files. The build artifacts will be stored in the `static-storybook/` directory and can be hosted on GitHub Pages or any other static resource server. ## Pipelines [Travis](travisURL) will build all PRs and provide feedback on lint, build, and test results.
Now that we have #13 in place (big shoutout to @vdemeester) for testing our code, we want to Docker build and push dashboard images: for example to gcr.io for anyone to pick up, test, experiment and use.
Important files to mention that I think will be of use: https://github.com/tektoncd/plumbing/blob/master/prow/config.yaml this denotes a "preSubmit" task that runs a script.
https://github.com/tektoncd/dashboard/blob/master/test/presubmit-tests.sh is how we currently run tests: this is said script for the "preSubmit" task.
If this was a Travis set up we would do the following, so we want to figure out how to do this with Prow.
script:
# - BUILD IMAGE
- export SHORT_COMMIT_ID=$(git rev-parse --short ${TRAVIS_COMMIT})
- docker build -t ${IMAGE_NAME}:${SHORT_COMMIT_ID} -f Dockerfile .
- docker build -t ${IMAGE_NAME}-test:${SHORT_COMMIT_ID} -f Dockerfile_test .
# - TAG/PUSH IMAGE
- if [[ "${TRAVIS_BRANCH}" == "master" ]] && [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then
docker login -u ${SOME_FUNCTIONAL_ID} -p ${SOME_API_KEY} ${REPO_NAME} ;
docker tag ${IMAGE_NAME}:${SHORT_COMMIT_ID} ${REPO_NAME}/${IMAGE_NAME}:${SHORT_COMMIT_ID} ;
docker push ${REPO_NAME}/${IMAGE_NAME}:${SHORT_COMMIT_ID} ;
docker tag ${IMAGE_NAME}:${SHORT_COMMIT_ID} ${REPO_NAME}/${IMAGE_NAME}:latest ;
docker push ${REPO_NAME}/${IMAGE_NAME}:latest ;
fi
Perhaps we add a "post submit" task that does the pushing?
If a step fails (using tektoncd/pipeline@master
), dashboard might report it as success even though it is not (hence the task status and the pipeline status in the following screenshot)
Looking at the status, it didn't look at the correct status for the step (the step exited with 1, but the status reported 1)
The screenshot is taken on a run based on the following files : https://sbr.pm/technical/tekton-usage.html#org63ddf32
/kind bug
Currently a POST
request to create a new gitresource returns 204 No Content
, whereas the general best practise for successful POST
requests is to return 201 Created
and to set the both data to the created data (ie, the POSTed data plus any additional fields like an ID that has been set).
An optional best practise is to also set the Location header to the URL that you would use to interact with that resource (ie. the URL you would use to GET
or DELETE
the resource).
Thanks guys, could I know the plan, many thanks! :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.