Skip to content

Commit

Permalink
Add about page to display versions
Browse files Browse the repository at this point in the history
  • Loading branch information
CarolynMabbott authored and tekton-robot committed Feb 10, 2020
1 parent 11124cf commit 3164395
Show file tree
Hide file tree
Showing 15 changed files with 471 additions and 3 deletions.
17 changes: 17 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,23 @@ Returns HTTP code 500 if an error occurred getting the extensions
Returns HTTP code 200 and the given extensions in the given namespace if found, otherwise an empty list is returned
```

__Dashboard Properties__
```
GET /v1/properties
Get the properties of the tekton-dashboard which includes the:
- Installation namespace
- Tekton Dashboard version
- Tekton Pipelines version
Example payload response is formatted as so:
{
"InstallNamespace": "tekton-pipelines",
"DashboardVersion": "v0.5.0",
"PipelineVersion": "v0.10.0"
}
```


## Extension

Expand Down
1 change: 1 addition & 0 deletions base/300-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ metadata:
namespace: tekton-pipelines
labels:
app: tekton-dashboard
version: development
spec:
replicas: 1
selector:
Expand Down
3 changes: 3 additions & 0 deletions packages/utils/src/utils/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export const paths = {
return `/extensions/${name}`;
}
},
about() {
return '/about';
},
importResources() {
return '/importresources';
},
Expand Down
4 changes: 4 additions & 0 deletions packages/utils/src/utils/router.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ describe('extensions', () => {
});
});

it('about', () => {
expect(urls.about()).toEqual(generatePath(paths.about()));
});

it('importResources', () => {
expect(urls.importResources()).toEqual(generatePath(paths.importResources()));
});
Expand Down
10 changes: 8 additions & 2 deletions pkg/endpoints/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
// Properties : properties we want to be able to retrieve via REST
type Properties struct {
InstallNamespace string
DashboardVersion string
PipelineVersion string
}

const (
Expand Down Expand Up @@ -157,8 +159,12 @@ func (r Resource) GetEndpoints(request *restful.Request, response *restful.Respo
}
}

// GetProperties is used to get the installed namespace only so far
// GetProperties is used to get the installed namespace, version of tekton dashboard and version of tekton pipelines
func (r Resource) GetProperties(request *restful.Request, response *restful.Response) {
properties := Properties{InstallNamespace: os.Getenv("INSTALLED_NAMESPACE")}
dashboardVersion := GetDashboardVersion(r)
pipelineVersion := GetPipelineVersion(r)

properties := Properties{InstallNamespace: os.Getenv("INSTALLED_NAMESPACE"), DashboardVersion: dashboardVersion, PipelineVersion: pipelineVersion}

response.WriteEntity(properties)
}
100 changes: 100 additions & 0 deletions pkg/endpoints/dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Copyright 2020 The Tekton Authors
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 endpoints

import (
"os"
"strings"

"github.com/tektoncd/dashboard/pkg/logging"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Get dashboard version
func GetDashboardVersion(r Resource) string {
properties := Properties{InstallNamespace: os.Getenv("INSTALLED_NAMESPACE")}
version := ""

listOptions := metav1.ListOptions{
LabelSelector: "app=tekton-dashboard",
}

deployments, err := r.K8sClient.AppsV1().Deployments(properties.InstallNamespace).List(listOptions)
if err != nil {
logging.Log.Errorf("Error getting dashboard deployment: %s", err.Error())
return ""
}

for _, deployment := range deployments.Items {
deploymentLabels := deployment.GetLabels()
versionAttempt, err := deploymentLabels["version"]
if err != true {
logging.Log.Error("Error getting dashboard version from yaml")
return ""
} else {
version = versionAttempt
}
}

if version == "" {
logging.Log.Error("Error getting the tekton dashboard deployment version. Version is unknown")
return ""
}
return version
}

// Get pipelines version
func GetPipelineVersion(r Resource) string {
version := ""

listOptions := metav1.ListOptions{
LabelSelector: "app.kubernetes.io/component=controller,app.kubernetes.io/name=tekton-pipelines",
}

deployments, err := r.K8sClient.AppsV1().Deployments("tekton-pipelines").List(listOptions)
if err != nil {
logging.Log.Errorf("Error getting the tekton pipelines deployment: %s", err.Error())
return ""
}

for _, deployment := range deployments.Items {
deploymentAnnotations := deployment.Spec.Template.GetAnnotations()

// For master of Tekton Pipelines
version = deploymentAnnotations["pipeline.tekton.dev/release"]

// For Tekton Pipelines 0.10.0 + 0.10.1
if version == "" {
version = deploymentAnnotations["tekton.dev/release"]
}

// For Tekton Pipelines 0.9.0 - 0.9.2
if version == "" {
deploymentImage := deployment.Spec.Template.Spec.Containers[0].Image
if strings.Contains(deploymentImage, "pipeline/cmd/controller") && strings.Contains(deploymentImage, ":") && strings.Contains(deploymentImage, "@") {
s := strings.SplitAfter(deploymentImage, ":")
if strings.Contains(s[1], "@") {
t := strings.Split(s[1], "@")
version = t[0]
}
}
}
}

if version == "" {
logging.Log.Error("Error getting the tekton pipelines deployment version. Version is unknown")
return ""
}
return version
}
1 change: 1 addition & 0 deletions pkg/router/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var excludeRoutes []string = []string{
"proxy", // Kube API server has its own standard
"ingress", // Ingress will not exist
"endpoints", // Route or Ingress will not exist
"properties", // Pods and namespace will not exist
}

var methodRouteMap = make(map[string][]string) // k, v := HTTP_METHOD, []route
Expand Down
164 changes: 164 additions & 0 deletions src/containers/About/About.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
Copyright 2020 The Tekton Authors
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.
*/

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { injectIntl } from 'react-intl';
import { Table } from '@tektoncd/dashboard-components';
import { InlineNotification } from 'carbon-components-react';
import { getInstallProperties } from '../../api';
import { isWebSocketConnected } from '../../reducers';

const initialState = {
dashboardInfo: {},
isLoaded: false,
isNotFinished: true,
error: ''
};

export /* istanbul ignore next */ class About extends Component {
constructor(props) {
super(props);
this.state = initialState;
}

componentDidMount() {
this.fetchDashboardInfo();
}

setErrorState(errorsFound, intl) {
const errorsFoundList = intl.formatMessage(
{
id: 'dashboard.error.error',
defaultMessage: `{errorsFound} cannot be found`
},
{ errorsFound }
);

if (this.state.isNotFinished) {
this.setState({
error: errorsFoundList,
isNotFinished: false
});
}
}

async fetchDashboardInfo() {
const dash = await getInstallProperties();
this.setState({
dashboardInfo: dash,
isLoaded: true
});
}

makeLines(property) {
let data = '';
const value = this.state.dashboardInfo[property];
if (this.state.dashboardInfo[property] !== undefined) {
data = {
id: property,
property,
value
};
}
return data;
}

render() {
const { intl, loading } = this.props;
const initialHeaders = [
{
key: 'property',
header: intl.formatMessage({
id: 'dashboard.tableHeader.property',
defaultMessage: 'Property'
})
},
{
key: 'value',
header: intl.formatMessage({
id: 'dashboard.tableHeader.value',
defaultMessage: 'Value'
})
}
];
const initialRows = [];
if (this.state.dashboardInfo !== '' && this.state.isLoaded === true) {
const errorsToDisplay = [];
const propertiesToCheck = [
'InstallNamespace',
'DashboardVersion',
'PipelineVersion'
];
propertiesToCheck.forEach(element => {
const line = this.makeLines(element);
if (line.value !== undefined && line.value !== '') {
initialRows.push(line);
} else {
errorsToDisplay.push(element);
}
});

if (errorsToDisplay.length !== 0) {
this.setErrorState(errorsToDisplay, intl);
}

return (
<>
{this.state.error !== '' && (
<InlineNotification
kind="error"
title={intl.formatMessage({
id: 'dashboard.displayVersion.error',
defaultMessage: 'Error getting version'
})}
subtitle={this.state.error}
lowContrast
/>
)}
<h1>
{intl.formatMessage({
id: 'dashboard.about.title',
defaultMessage: 'About'
})}
</h1>
<Table
headers={initialHeaders}
rows={initialRows}
loading={loading}
/>
</>
);
}

return (
<>
<h1>
{intl.formatMessage({
id: 'dashboard.about.title',
defaultMessage: 'About'
})}
</h1>
<Table headers={initialHeaders} rows={initialRows} loading={loading} />
</>
);
}
}

function mapStateToProps(state) {
return {
webSocketConnected: isWebSocketConnected(state)
};
}

export default connect(mapStateToProps)(injectIntl(About));
Loading

0 comments on commit 3164395

Please sign in to comment.