mirror of https://github.com/linkerd/linkerd2.git
Implement client-side version checking (#79)
Previously Conduit would render an iframe, received from versioncheck.conduit.io. Modify the client to retrieve the latest released version, via CORS. Signed-off-by: Andrew Seigner <andrew@sig.gy>
This commit is contained in:
parent
ce69c2e534
commit
449f306aeb
|
@ -43,6 +43,13 @@ And then set the `-webpack-dev-server` flag when running the web server:
|
|||
go run main.go -webpack-dev-server=http://localhost:8080
|
||||
```
|
||||
|
||||
To add a JS dependency:
|
||||
|
||||
```
|
||||
cd web/app
|
||||
yarn add [dep]
|
||||
```
|
||||
|
||||
## Run docker-compose
|
||||
|
||||
You can also run all of the go apps in a docker-compose environment.
|
||||
|
|
|
@ -65,24 +65,6 @@
|
|||
& .ant-menu-item-active, & .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background: #007EFF;
|
||||
}
|
||||
|
||||
& .conduit-current-version {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0 0 0 9px;
|
||||
position: fixed;
|
||||
height: 40px;
|
||||
width: 150px;
|
||||
bottom: calc(var(--base-width)*3);
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
& .conduit-version-check {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.call-to-action .action {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
--darkblue: #071E3C;
|
||||
--warmgrey: #f6f6f6;
|
||||
--coldgrey: #c9c9c9;
|
||||
--latency-p99: #2F80ED;
|
||||
--latency-p99: var(--messageblue);
|
||||
--latency-p95: #2D9CDB;
|
||||
--latency-p50: #56CCF2;
|
||||
--subheader-grey: #828282;
|
||||
|
@ -185,3 +185,40 @@ tr th.numeric, .numeric {
|
|||
text-decoration-style: dotted;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* from https://conduit.io/ */
|
||||
|
||||
a.button {
|
||||
color: var(--messageblue);
|
||||
background: #fff;
|
||||
border-radius: 48px;
|
||||
border: 1px solid var(--messageblue);
|
||||
padding: 10px 14px 11px 14px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
margin: 0px 8px 8px 0px;
|
||||
position: relative;
|
||||
line-height: 4;
|
||||
}
|
||||
a.button:hover {
|
||||
top: 1px;
|
||||
background-color: #efefef;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.button:active {
|
||||
background-color: var(--messageblue);
|
||||
color: #fff;
|
||||
}
|
||||
a.button.primary {
|
||||
color: #fff;
|
||||
background-color: var(--messageblue);
|
||||
}
|
||||
a.button.primary:hover {
|
||||
top: 1px;
|
||||
background-color: #2875DE;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.button.primary:active {
|
||||
background-color: #2469C7;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
@import 'styles.css';
|
||||
|
||||
.version {
|
||||
padding: 0 0 0 9px;
|
||||
font-size: 13px;
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: 2;
|
||||
position: fixed;
|
||||
height: 40px;
|
||||
bottom: calc(var(--base-width)*10);
|
||||
}
|
|
@ -2,6 +2,7 @@ import { Link } from 'react-router-dom';
|
|||
import logo from './../../img/reversed_logo.png';
|
||||
import { Menu } from 'antd';
|
||||
import React from 'react';
|
||||
import Version from './Version.jsx';
|
||||
import './../../css/sidebar.css';
|
||||
|
||||
export default class Sidebar extends React.Component {
|
||||
|
@ -28,13 +29,9 @@ export default class Sidebar extends React.Component {
|
|||
<Link to="https://conduit.io/docs/" target="_blank">Documentation</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
<div className="conduit-current-version">
|
||||
Running {this.props.releaseVersion}<br />
|
||||
<iframe
|
||||
className="conduit-version-check"
|
||||
src={`https://versioncheck.conduit.io/version/${this.props.releaseVersion}?uuid=${this.props.uuid}`}
|
||||
sandbox="allow-top-navigation" />
|
||||
</div>
|
||||
<Version
|
||||
releaseVersion={this.props.releaseVersion}
|
||||
uuid={this.props.uuid} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import { ApiHelpers } from './util/ApiHelpers.js';
|
||||
import { Link } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
import './../../css/version.css';
|
||||
|
||||
export default class Version extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.loadFromServer = this.loadFromServer.bind(this);
|
||||
this.handleApiError = this.handleApiError.bind(this);
|
||||
this.state = {
|
||||
error: null,
|
||||
latest: null,
|
||||
loaded: false,
|
||||
pendingRequests: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadFromServer();
|
||||
}
|
||||
|
||||
loadFromServer() {
|
||||
if (this.state.pendingRequests) {
|
||||
return; // don't make more requests if the ones we sent haven't completed
|
||||
}
|
||||
this.setState({ pendingRequests: true });
|
||||
|
||||
let versionUrl = `https://versioncheck.conduit.io/version.json?version=${this.props.releaseVersion}?uuid=${this.props.uuid}`;
|
||||
let versionFetch = ApiHelpers("").fetch(versionUrl);
|
||||
// expose serverPromise for testing
|
||||
this.serverPromise = Promise.all([versionFetch])
|
||||
.then(([resp]) => {
|
||||
this.setState({
|
||||
latest: resp.version,
|
||||
loaded: true,
|
||||
pendingRequests: false,
|
||||
});
|
||||
}).catch(this.handleApiError);
|
||||
}
|
||||
|
||||
handleApiError(e) {
|
||||
this.setState({
|
||||
loaded: true,
|
||||
pendingRequests: false,
|
||||
error: e
|
||||
});
|
||||
}
|
||||
|
||||
renderVersionCheck() {
|
||||
if (!this.state.loaded || this.state.pendingRequests) {
|
||||
return "Performing version check...";
|
||||
}
|
||||
|
||||
if (!this.state.latest) {
|
||||
return (<div>
|
||||
Version check failed
|
||||
{this.state.error ? ": "+this.state.error : null}
|
||||
</div>);
|
||||
} else if (this.state.latest === this.props.releaseVersion) {
|
||||
return "Conduit is up to date";
|
||||
} else {
|
||||
return (<div>
|
||||
A new version ({this.state.latest}) is available<br />
|
||||
<Link to="https://versioncheck.conduit.io/update" className="button primary" target="_blank">Update Now</Link>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="version">
|
||||
Currently running Conduit {this.props.releaseVersion}<br />
|
||||
{this.renderVersionCheck()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { expect } from 'chai';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import sinonStubPromise from 'sinon-stub-promise';
|
||||
import Version from '../js/components/Version.jsx';
|
||||
|
||||
sinonStubPromise(sinon);
|
||||
|
||||
describe('Version', () => {
|
||||
let curVer = "v1.2.3";
|
||||
let newVer = "v2.3.4";
|
||||
|
||||
let component, fetchStub;
|
||||
|
||||
function withPromise(fn) {
|
||||
return component.find("Version").get(0).serverPromise.then(fn);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
fetchStub = sinon.stub(window, 'fetch');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.fetch.restore();
|
||||
});
|
||||
|
||||
it('renders initial loading message', () => {
|
||||
fetchStub.returnsPromise().resolves({
|
||||
ok: true,
|
||||
});
|
||||
|
||||
component = mount(
|
||||
<BrowserRouter>
|
||||
<Version />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(component.find("Version")).to.have.length(1);
|
||||
expect(component.html()).to.include("Performing version check...");
|
||||
});
|
||||
|
||||
it('renders up to date message when versions match', () => {
|
||||
fetchStub.returnsPromise().resolves({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ version: curVer })
|
||||
});
|
||||
|
||||
component = mount(
|
||||
<BrowserRouter>
|
||||
<Version
|
||||
releaseVersion={curVer}
|
||||
uuid="fakeuuid" />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
return withPromise(() => {
|
||||
expect(component.html()).to.include("Conduit is up to date");
|
||||
});
|
||||
});
|
||||
|
||||
it('renders update message when versions do not match', () => {
|
||||
fetchStub.returnsPromise().resolves({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ version: newVer })
|
||||
});
|
||||
|
||||
component = mount(
|
||||
<BrowserRouter>
|
||||
<Version
|
||||
releaseVersion={curVer}
|
||||
uuid="fakeuuid" />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
return withPromise(() => {
|
||||
expect(component.html()).to.include("A new version (");
|
||||
expect(component.html()).to.include(newVer);
|
||||
expect(component.html()).to.include(") is available");
|
||||
});
|
||||
});
|
||||
|
||||
it('renders error when version check fails', () => {
|
||||
let errMsg = "Fake error";
|
||||
|
||||
fetchStub.returnsPromise().resolves({
|
||||
ok: false,
|
||||
statusText: errMsg
|
||||
});
|
||||
|
||||
component = mount(
|
||||
<BrowserRouter>
|
||||
<Version />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
return withPromise(() => {
|
||||
expect(component.html()).to.include("Version check failed");
|
||||
expect(component.html()).to.include(errMsg);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue