Skip to content

Commit

Permalink
Components with actions only show when not in read-only mode
Browse files Browse the repository at this point in the history
  • Loading branch information
a-roberts authored and tekton-robot committed Mar 6, 2020
1 parent 18a2549 commit c833537
Show file tree
Hide file tree
Showing 27 changed files with 538 additions and 121 deletions.
8 changes: 5 additions & 3 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Development dashboard builds come in four flavours: (plain kube or Openshift) *
```shell
# Plain Kube
kustomize build overlays/dev | ko apply -f -
kustomize build overlays/dev-locked-down | ko apply -f -
kustomize build --load_restrictor=LoadRestrictionsNone overlays/dev-locked-down | ko apply -f -

# OpenShift
kustomize build overlays/dev-openshift --load_restrictor=LoadRestrictionsNone \
Expand Down Expand Up @@ -267,8 +267,10 @@ Example payload response is formatted as so:
{
"InstallNamespace": "tekton-pipelines",
"DashboardVersion": "v0.5.0",
"PipelineVersion": "v0.10.0"
"DashboardVersion": "development",
"PipelineVersion": "v0.10.0",
"IsOpenShift": false,
"ReadOnly": true
}
```

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ kustomize build overlays/latest-openshift --load_restrictor=LoadRestrictionsNone
kustomize build overlays/latest-openshift-locked-down --load_restrictor=LoadRestrictionsNone \
| ko resolve -f - | kubectl apply -f - --validate=false
```

In read-only mode, buttons and sections of the Dashboard will not be displayed (for example, you won't have the ability to create, stop, and delete PipelineRuns).

Development installation of the Dashboard uses `ko`:

```bash
Expand Down
2 changes: 2 additions & 0 deletions base/300-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ spec:
env:
- name: PORT
value: "9097"
- name: READ_ONLY
value: "false"
- name: WEB_RESOURCES_DIR
value: /var/run/ko/web
- name: PIPELINE_RUN_SERVICE_ACCOUNT
Expand Down
4 changes: 4 additions & 0 deletions overlays/dev-locked-down/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
images:
- name: dashboardImage
newName: github.com/tektoncd/dashboard/cmd/dashboard
newTag:
patches:
- ../readonly/readonly-deployment-patch.yaml
1 change: 1 addition & 0 deletions overlays/dev-openshift-locked-down/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ patches:
- ../openshift-patches/dashboard-service.yaml
- ../openshift-patches/serviceaccount.yaml
- ../openshift-patches/oauth-proxy-in-deployment.yaml
- ../readonly/readonly-deployment-patch.yaml
images:
- name: dashboardImage
newName: github.com/tektoncd/dashboard/cmd/dashboard
Expand Down
2 changes: 2 additions & 0 deletions overlays/latest-locked-down/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ images:
- name: dashboardImage
newName: gcr.io/tekton-nightly/dashboard
newTag: latest
patches:
- ../readonly/readonly-deployment-patch.yaml
1 change: 1 addition & 0 deletions overlays/latest-openshift-locked-down/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ patches:
- ../openshift-patches/dashboard-service.yaml
- ../openshift-patches/serviceaccount.yaml
- ../openshift-patches/oauth-proxy-in-deployment.yaml
- ../readonly/readonly-deployment-patch.yaml
images:
- name: dashboardImage
newName: gcr.io/tekton-nightly/dashboard
Expand Down
14 changes: 14 additions & 0 deletions overlays/readonly/readonly-deployment-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tekton-dashboard
namespace: tekton-pipelines
spec:
template:
spec:
containers:
- name: tekton-dashboard
env:
- name: READ_ONLY
value: "true"
16 changes: 13 additions & 3 deletions pkg/endpoints/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Properties struct {
DashboardVersion string
PipelineVersion string
IsOpenShift bool
ReadOnly bool
}

const (
Expand Down Expand Up @@ -160,14 +161,23 @@ func (r Resource) GetEndpoints(request *restful.Request, response *restful.Respo
}
}

// GetProperties is used to get the installed namespace, version of tekton dashboard and version of tekton pipelines
// GetProperties is used to get the installed namespace for the Dashboard,
// the version of the Tekton Dashboard, the version of Tekton Pipelines, whether or not one's
// running on OpenShift and when one's in read-only mode
func (r Resource) GetProperties(request *restful.Request, response *restful.Response) {
installedNamespace := os.Getenv("INSTALLED_NAMESPACE")
dashboardVersion := GetDashboardVersion(r, installedNamespace)
isOpenShift := IsOpenShift(r, installedNamespace)
isReadOnly := IsReadOnly()
pipelineVersion := GetPipelineVersion(r, isOpenShift)

properties := Properties{InstallNamespace: installedNamespace, DashboardVersion: dashboardVersion, PipelineVersion: pipelineVersion, IsOpenShift: isOpenShift}

properties := Properties{
InstallNamespace: os.Getenv("INSTALLED_NAMESPACE"),
DashboardVersion: dashboardVersion,
PipelineVersion: pipelineVersion,
IsOpenShift: isOpenShift,
ReadOnly: isReadOnly,
}
logging.Log.Debugf("Writing install properties: %s", properties)
response.WriteEntity(properties)
}
15 changes: 14 additions & 1 deletion pkg/endpoints/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ limitations under the License.
package endpoints

import (
"os"
"strconv"
"strings"

"github.com/tektoncd/dashboard/pkg/logging"
Expand Down Expand Up @@ -77,7 +79,18 @@ func GetPipelineVersion(r Resource, isOpenShift bool) string {
return version
}

// Get whether running on openshift or not
// IsReadOnly determines whether the Dashboard is running in read-only mode or not
func IsReadOnly() bool {
asBool, err := strconv.ParseBool(os.Getenv("READ_ONLY"))
if err == nil {
logging.Log.Infof("Dashboard is in read-only mode: %s", asBool)
return asBool
}
logging.Log.Warnf("Couldn't determine if the Dashboard is in read-only mode or not, assuming not")
return false
}

// IsOpenShift determines whether the Dashboard is running on OpenShift or not
func IsOpenShift(r Resource, installedNamespace string) bool {
namespaces, err := r.K8sClient.CoreV1().Namespaces().List(v1.ListOptions{})
if err != nil {
Expand Down
27 changes: 27 additions & 0 deletions src/actions/properties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
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 { getInstallProperties } from '../api';

export function fetchInstallProperties() {
return async dispatch => {
let data;
try {
data = await getInstallProperties();
dispatch({ type: 'INSTALL_PROPERTIES_SUCCESS', data });
} catch (error) {
dispatch({ type: 'INSTALL_PROPERTIES_FAILURE', error });
}
return data;
};
}
3 changes: 2 additions & 1 deletion src/containers/About/About.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const propertiesToCheck = [
'InstallNamespace',
'DashboardVersion',
'PipelineVersion',
'IsOpenShift'
'IsOpenShift',
'ReadOnly'
];

export /* istanbul ignore next */ class About extends Component {
Expand Down
32 changes: 16 additions & 16 deletions src/containers/About/About.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ describe('About', () => {
InstallNamespace: 'tekton-pipelines',
DashboardVersion: 'v0.100.0',
PipelineVersion: 'v0.10.0',
IsOpenShift: true
IsOpenShift: true,
ReadOnly: true
}));

const { queryByText } = renderWithIntl(<About />);
Expand All @@ -39,39 +40,38 @@ describe('About', () => {
expect(queryByText('PipelineVersion')).toBeTruthy();
expect(queryByText('v0.10.0')).toBeTruthy();
expect(queryByText('IsOpenShift')).toBeTruthy();
expect(queryByText('ReadOnly')).toBeTruthy();
expect(queryByText('True')).toBeTruthy();
});

it('should render error when an expected property is missing', async () => {
const installProperties = Promise.resolve({
InstallNamespace: 'tekton-pipelines',
DashboardVersion: '',
// DashboardVersion: '', this is intentionally missing
PipelineVersion: 'v0.10.0',
IsOpenShift: false
IsOpenShift: false,
ReadOnly: false
});
jest
.spyOn(API, 'getInstallProperties')
.mockImplementation(() => installProperties);

const { queryByText } = renderWithIntl(<About />);
const { getByText, queryByText } = renderWithIntl(<About />);

await waitForElement(() => queryByText('Property'));
expect(API.getInstallProperties).toHaveBeenCalledTimes(1);
expect(queryByText('Value')).toBeTruthy();
expect(queryByText('InstallNamespace')).toBeTruthy();
expect(queryByText('PipelineVersion')).toBeTruthy();
expect(queryByText('tekton-pipelines')).toBeTruthy();
expect(queryByText('v0.10.0')).toBeTruthy();
expect(queryByText('Error getting data')).toBeTruthy();
expect(queryByText('Could not find: DashboardVersion')).toBeTruthy();
expect(queryByText('IsOpenShift')).toBeFalsy();
expect(getByText('Value')).toBeTruthy();
expect(getByText('InstallNamespace')).toBeTruthy();
expect(getByText('PipelineVersion')).toBeTruthy();
expect(getByText('tekton-pipelines')).toBeTruthy();
expect(getByText('v0.10.0')).toBeTruthy();
expect(getByText('Error getting data')).toBeTruthy();
expect(getByText('Could not find: DashboardVersion')).toBeTruthy();
});

it('should render error when multiple expected properties are missing', async () => {
jest.spyOn(API, 'getInstallProperties').mockImplementation(() => ({
InstallNamespace: 'tekton-pipelines',
DashboardVersion: '',
PipelineVersion: ''
InstallNamespace: 'tekton-pipelines'
}));

const { queryByText } = renderWithIntl(<About />);
Expand All @@ -85,7 +85,7 @@ describe('About', () => {
expect(queryByText('Error getting data')).toBeTruthy();
expect(
queryByText(
'Could not find: DashboardVersion, PipelineVersion, IsOpenShift'
'Could not find: DashboardVersion, PipelineVersion, IsOpenShift, ReadOnly'
)
).toBeTruthy();
});
Expand Down
16 changes: 16 additions & 0 deletions src/containers/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {

import { IntlProvider } from 'react-intl';
import { Content } from 'carbon-components-react';

import {
Header,
LogoutButton,
Expand Down Expand Up @@ -63,9 +64,12 @@ import {
import { shouldDisplayLogout } from '../../api';
import { fetchExtensions } from '../../actions/extensions';
import { fetchNamespaces, selectNamespace } from '../../actions/namespaces';
import { fetchInstallProperties } from '../../actions/properties';

import {
getExtensions,
getLocale,
getReadOnly,
getSelectedNamespace,
isWebSocketConnected
} from '../../reducers';
Expand All @@ -92,11 +96,13 @@ export /* istanbul ignore next */ class App extends Component {

fetchData() {
this.props.fetchExtensions();
this.props.fetchInstallProperties();
this.props.fetchNamespaces();
}

render() {
const { extensions } = this.props;

const lang = messages[this.props.lang] ? this.props.lang : 'en';

const logoutButton = (
Expand Down Expand Up @@ -190,6 +196,14 @@ export /* istanbul ignore next */ class App extends Component {
path={paths.importResources()}
component={ImportResources}
/>

{!this.props.isReadOnly && (
<Route
path={paths.importResources()}
component={ImportResources}
/>
)}

<Route path={paths.secrets.all()} exact component={Secrets} />
<Route
path={paths.serviceAccounts.byName()}
Expand Down Expand Up @@ -334,12 +348,14 @@ const mapStateToProps = state => ({
extensions: getExtensions(state),
namespace: getSelectedNamespace(state),
lang: getLocale(state),
isReadOnly: getReadOnly(state),
webSocketConnected: isWebSocketConnected(state)
});

const mapDispatchToProps = {
fetchExtensions,
fetchNamespaces,
fetchInstallProperties,
selectNamespace
};

Expand Down
3 changes: 3 additions & 0 deletions src/containers/App/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import thunk from 'redux-thunk';

import { App } from './App';
import * as API from '../../api';
import * as Reducers from '../../reducers';

beforeEach(() => {
jest.spyOn(API, 'getPipelines').mockImplementation(() => {});
jest.spyOn(Reducers, 'getReadOnly').mockImplementation(() => true);
});

it('App renders successfully', () => {
Expand All @@ -42,6 +44,7 @@ it('App renders successfully', () => {
extensions={[]}
fetchExtensions={() => {}}
fetchNamespaces={() => {}}
fetchInstallProperties={() => {}}
/>
</Provider>
);
Expand Down
Loading

0 comments on commit c833537

Please sign in to comment.