Skip to content
This repository was archived by the owner on Dec 4, 2024. It is now read-only.

Commit b51c1d2

Browse files
committed
Use MARATHON_SCHEME_PORT label to define scheme of a given port
The links in the tasks view use the same scheme as the Marathon UI web application but in some cases users expose a service with a different scheme on a given port. In order to correctly redirect the user clicking on the link of a given port, one can use MARATHON_SCHEME_PORT and MARATHON_SCHEME_PORT<N> labels to define the scheme of the all the ports and/or the scheme of port with index N. The available values for those labels are 'http' and 'https'. If no label is provided, the scheme is the same as Marathon UI scheme to avoid breaking compatibility.
1 parent cee3b3d commit b51c1d2

7 files changed

+191
-7
lines changed

src/js/components/AppPageComponent.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ var AppPageComponent = React.createClass({
352352
fetchState={state.fetchState}
353353
getTaskHealthMessage={this.getTaskHealthMessage}
354354
hasHealth={Object.keys(model.healthChecks).length > 0}
355+
labels={model.labels}
355356
tasks={model.tasks} />
356357
</TabPaneComponent>
357358
<TabPaneComponent

src/js/components/TaskListComponent.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var TaskListComponent = React.createClass({
1717
getTaskHealthMessage: React.PropTypes.func.isRequired,
1818
hasHealth: React.PropTypes.bool,
1919
itemsPerPage: React.PropTypes.number.isRequired,
20+
labels: React.PropTypes.object.isRequired,
2021
onTaskToggle: React.PropTypes.func.isRequired,
2122
selectedTasks: React.PropTypes.object.isRequired,
2223
tasks: React.PropTypes.array.isRequired,
@@ -70,6 +71,7 @@ var TaskListComponent = React.createClass({
7071
onToggle={props.onTaskToggle}
7172
sortKey={sortKey}
7273
task={task}
74+
labels={props.labels}
7375
taskHealthMessage={props.getTaskHealthMessage(task.id)} />
7476
);
7577
})

src/js/components/TaskListItemComponent.jsx

+22-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import AppsStore from "../stores/AppsStore";
77
import HealthStatus from "../constants/HealthStatus";
88
import TaskStatus from "../constants/TaskStatus";
99
import TaskFileDownloadComponent from "../components/TaskFileDownloadComponent";
10+
import ServiceSchemeUtil from "../helpers/ServiceSchemeUtil";
11+
import { watch } from "fs";
1012

1113
function joinNodes(nodes, separator = ", ") {
1214
var lastIndex = nodes.length - 1;
@@ -22,13 +24,20 @@ function joinNodes(nodes, separator = ", ") {
2224
});
2325
}
2426

27+
function getPortScheme(labels, portIndex) {
28+
const scheme = ServiceSchemeUtil
29+
.getServiceSchemeFromLabels(labels, portIndex);
30+
return scheme == '' ? '//' : `${scheme}://`;
31+
}
32+
2533
var TaskListItemComponent = React.createClass({
2634
displayName: "TaskListItemComponent",
2735

2836
propTypes: {
2937
appId: React.PropTypes.string.isRequired,
3038
hasHealth: React.PropTypes.bool,
3139
isActive: React.PropTypes.bool.isRequired,
40+
labels: React.PropTypes.object.isRequired,
3241
onToggle: React.PropTypes.func.isRequired,
3342
sortKey: React.PropTypes.string,
3443
task: React.PropTypes.object.isRequired,
@@ -37,28 +46,31 @@ var TaskListItemComponent = React.createClass({
3746

3847
getHostAndPorts: function () {
3948
var task = this.props.task;
49+
var props = this.props;
4050
var ports = task.ports;
4151

4252
if (ports == null || ports.length === 0 ) {
4353
return (<span className="text-muted">{task.host}</span>);
4454
}
4555

4656
if (ports != null && ports.length === 1) {
57+
const scheme = getPortScheme(props.labels, 0);
4758
return (
4859
<a className="text-muted"
49-
href={`//${task.host}:${ports[0]}`}
60+
href={`${scheme}${task.host}:${ports[0]}`}
5061
target="_blank">
5162
{`${task.host}:${ports[0]}`}
5263
</a>
5364
);
5465
}
5566

5667
if (ports != null && ports.length > 1) {
57-
let portNodes = ports.map(function (port) {
68+
let portNodes = ports.map(function (port, i) {
69+
const scheme = getPortScheme(props.labels, i);
5870
return (
5971
<a key={`${task.host}:${port}`}
6072
className="text-muted"
61-
href={`//${task.host}:${port}`}
73+
href={`${scheme}${task.host}:${port}`}
6274
target="_blank">
6375
{port}
6476
</a>
@@ -98,18 +110,22 @@ var TaskListItemComponent = React.createClass({
98110
let ipAddress = address.ipAddress;
99111
if (serviceDiscoveryPorts.length === 1) {
100112
let port = serviceDiscoveryPorts[0].number;
113+
const scheme = getPortScheme(props.labels, 0);
101114
return (
102115
<a key={`${ipAddress}:${port}`}
103-
className="text-muted" href={`//${ipAddress}:${port}`}>
116+
className="text-muted"
117+
href={`${scheme}${ipAddress}:${port}`}>
104118
{`${ipAddress}:${port}`}
105119
</a>
106120
);
107121
}
108122

109-
let portNodes = serviceDiscoveryPorts.map((port) => {
123+
let portNodes = serviceDiscoveryPorts.map((port, i) => {
124+
const scheme = getPortScheme(props.labels, i);
110125
return (
111126
<a key={`${ipAddress}:${port.number}`}
112-
className="text-muted" href={`//${ipAddress}:${port.number}`}>
127+
className="text-muted"
128+
href={`${scheme}${ipAddress}:${port.number}`}>
113129
{port.number}
114130
</a>
115131
);

src/js/components/TaskViewComponent.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var TaskViewComponent = React.createClass({
1818
fetchState: React.PropTypes.number.isRequired,
1919
getTaskHealthMessage: React.PropTypes.func.isRequired,
2020
hasHealth: React.PropTypes.bool,
21+
labels: React.PropTypes.object.isRequired,
2122
tasks: React.PropTypes.array.isRequired
2223
},
2324

@@ -228,6 +229,7 @@ var TaskViewComponent = React.createClass({
228229
hasHealth={this.props.hasHealth}
229230
onTaskToggle={this.onTaskToggle}
230231
itemsPerPage={this.state.itemsPerPage}
232+
labels={this.props.labels}
231233
selectedTasks={this.state.selectedTasks}
232234
tasks={this.props.tasks}
233235
toggleAllTasks={this.toggleAllTasks} />

src/js/helpers/ServiceSchemeUtil.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const BASE_SCHEME_LABEL = "MARATHON_SCHEME_PORT";
2+
3+
var ServiceSchemeUtil = {
4+
/*
5+
* Returns service scheme of the n-th port.
6+
*
7+
* Given N a port index, if `MARATHON_SCHEME_PORT<N>` is
8+
* in the set of labels, then the function returns the value
9+
* of this label.
10+
*
11+
* Given N a port index, if `MARATHON_SCHEME_PORT0<N>` is
12+
* not in the set of labels, then the value associated with
13+
* `MARATHON_SCHEME_PORT` is returned.
14+
*
15+
* Given N a port index, if `MARATHON_SCHEME_PORT0<N>` and
16+
* `MARATHON_SCHEME_PORT` are not in the set of labels, the
17+
* function returns the `http` as the default scheme.
18+
*/
19+
getServiceSchemeFromLabels(labels, n) {
20+
function getScheme(labelValue) {
21+
return (labelValue === "http" || labelValue === "https")
22+
? labelValue
23+
: '';
24+
}
25+
26+
const labelKey = ("" + BASE_SCHEME_LABEL + n);
27+
if (labels && labelKey in labels)
28+
return getScheme(labels[labelKey]);
29+
else if (labels && BASE_SCHEME_LABEL in labels)
30+
return getScheme(labels[BASE_SCHEME_LABEL]);
31+
32+
return '';
33+
}
34+
};
35+
36+
export default ServiceSchemeUtil;
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {expect} from "chai";
2+
import ServiceSchemeUtil from "../../js/helpers/ServiceSchemeUtil";
3+
4+
function verifyPortSchemeWithDefault(serviceScheme, serviceScheme0,
5+
expectedSchemePort0, expectedSchemePort1) {
6+
describe("MARATHON_SCHEME_PORT is set to " + serviceScheme, function() {
7+
describe("MARATHON_SCHEME_PORT0 is set to " + serviceScheme0, function() {
8+
it("detect scheme of port 0", function() {
9+
expect(ServiceSchemeUtil.getServiceSchemeFromLabels({
10+
"MARATHON_SCHEME_PORT": serviceScheme,
11+
"MARATHON_SCHEME_PORT0": serviceScheme0,
12+
}, 0)).to.eq(expectedSchemePort0);
13+
});
14+
15+
it("detect scheme of port 1", function() {
16+
expect(ServiceSchemeUtil.getServiceSchemeFromLabels({
17+
"MARATHON_SCHEME_PORT": serviceScheme,
18+
"MARATHON_SCHEME_PORT0": serviceScheme0,
19+
}, 1)).to.eq(expectedSchemePort1);
20+
});
21+
});
22+
});
23+
}
24+
25+
function verifyPortSchemeWithoutDefault(serviceScheme0,
26+
expectedSchemePort0, expectedSchemePort1) {
27+
describe("MARATHON_SCHEME_PORT is not set", function() {
28+
describe("MARATHON_SCHEME_PORT0 is set to " + serviceScheme0, function() {
29+
it("detect scheme of port 0", function() {
30+
expect(ServiceSchemeUtil.getServiceSchemeFromLabels({
31+
"MARATHON_SCHEME_PORT0": serviceScheme0,
32+
}, 0)).to.eq(expectedSchemePort0);
33+
});
34+
35+
it("detect scheme of port 1", function() {
36+
expect(ServiceSchemeUtil.getServiceSchemeFromLabels({
37+
"MARATHON_SCHEME_PORT0": serviceScheme0,
38+
}, 1)).to.eq(expectedSchemePort1);
39+
});
40+
});
41+
});
42+
}
43+
44+
describe("ServiceSchemeUtil", function () {
45+
// default scheme scheme port 0 expected port 0 expected port 1
46+
verifyPortSchemeWithDefault("http", "http", "http", "http");
47+
verifyPortSchemeWithDefault("http", "https", "https", "http");
48+
verifyPortSchemeWithDefault("https", "http", "http", "https");
49+
verifyPortSchemeWithDefault("https", "https", "https", "https");
50+
51+
// scheme port 0 expected port 0 expected port 1
52+
verifyPortSchemeWithoutDefault("http", "http", "");
53+
verifyPortSchemeWithoutDefault("https", "https", "");
54+
verifyPortSchemeWithoutDefault("http", "http", "");
55+
verifyPortSchemeWithoutDefault("https", "https", "");
56+
});

src/test/units/TaskListItemComponent.test.js

+72-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe("Task List Item component", function () {
1111
appId: "/app-1",
1212
id: "task-123",
1313
host: "host-1",
14-
ports: [1, 2, 3],
14+
ports: [8081, 8082, 8083],
1515
status: "status-0",
1616
updatedAt: "2015-06-29T14:11:58.709Z",
1717
version: "2015-06-29T13:54:24.171Z"
@@ -20,6 +20,7 @@ describe("Task List Item component", function () {
2020
this.component = shallow(
2121
<TaskListItemComponent appId={"/app-1"}
2222
hasHealth={true}
23+
labels={{}}
2324
taskHealthMessage="Healthy"
2425
isActive={false}
2526
onToggle={()=>{}}
@@ -37,6 +38,76 @@ describe("Task List Item component", function () {
3738
).to.equal("task-123");
3839
});
3940

41+
describe("task url are correct", function() {
42+
function getNthPortLink(component, n) {
43+
return component.find("td")
44+
.at(1).children()
45+
.at(2).children()
46+
.at(2 + n).children().first().props().href
47+
}
48+
49+
it("has a HTTP task url when app does not have scheme label", function() {
50+
expect(getNthPortLink(this.component, 0)).to.equal("//host-1:8081");
51+
expect(getNthPortLink(this.component, 1)).to.equal("//host-1:8082");
52+
expect(getNthPortLink(this.component, 2)).to.equal("//host-1:8083");
53+
});
54+
55+
it("has only https schemes", function() {
56+
var model = {
57+
appId: "/app-1",
58+
id: "task-123",
59+
host: "host-1",
60+
ports: [8081, 8082, 8083],
61+
status: "status-0",
62+
updatedAt: "2015-06-29T14:11:58.709Z",
63+
version: "2015-06-29T13:54:24.171Z"
64+
};
65+
66+
this.component = shallow(
67+
<TaskListItemComponent appId={"/app-1"}
68+
hasHealth={true}
69+
taskHealthMessage="Healthy"
70+
isActive={false}
71+
labels={{
72+
"MARATHON_SCHEME_PORT": "https"
73+
}}
74+
onToggle={()=>{}}
75+
task={model} />
76+
);
77+
expect(getNthPortLink(this.component, 0)).to.equal("https://host-1:8081");
78+
expect(getNthPortLink(this.component, 1)).to.equal("https://host-1:8082");
79+
expect(getNthPortLink(this.component, 2)).to.equal("https://host-1:8083");
80+
})
81+
82+
it("has different schemes depending on the port", function() {
83+
var model = {
84+
appId: "/app-1",
85+
id: "task-123",
86+
host: "host-1",
87+
ports: [8081, 8082, 8083],
88+
status: "status-0",
89+
updatedAt: "2015-06-29T14:11:58.709Z",
90+
version: "2015-06-29T13:54:24.171Z"
91+
};
92+
93+
this.component = shallow(
94+
<TaskListItemComponent appId={"/app-1"}
95+
hasHealth={true}
96+
taskHealthMessage="Healthy"
97+
isActive={false}
98+
labels={{
99+
"MARATHON_SCHEME_PORT0": "https",
100+
"MARATHON_SCHEME_PORT2": "http"
101+
}}
102+
onToggle={()=>{}}
103+
task={model} />
104+
);
105+
expect(getNthPortLink(this.component, 0)).to.equal("https://host-1:8081");
106+
expect(getNthPortLink(this.component, 1)).to.equal("//host-1:8082");
107+
expect(getNthPortLink(this.component, 2)).to.equal("http://host-1:8083");
108+
})
109+
})
110+
40111
it("has correct health message", function () {
41112
expect(this.component.find("td").at(2).text()).to.equal("Healthy");
42113
});

0 commit comments

Comments
 (0)