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

Commit 800a106

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 800a106

File tree

7 files changed

+190
-7
lines changed

7 files changed

+190
-7
lines changed

src/js/components/AppPageComponent.jsx

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ 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";
1011

1112
function joinNodes(nodes, separator = ", ") {
1213
var lastIndex = nodes.length - 1;
@@ -22,13 +23,20 @@ function joinNodes(nodes, separator = ", ") {
2223
});
2324
}
2425

26+
function getPortScheme(labels, portIndex) {
27+
const scheme = ServiceSchemeUtil
28+
.getServiceSchemeFromLabels(labels, portIndex);
29+
return scheme === "" ? "//" : `${scheme}://`;
30+
}
31+
2532
var TaskListItemComponent = React.createClass({
2633
displayName: "TaskListItemComponent",
2734

2835
propTypes: {
2936
appId: React.PropTypes.string.isRequired,
3037
hasHealth: React.PropTypes.bool,
3138
isActive: React.PropTypes.bool.isRequired,
39+
labels: React.PropTypes.object.isRequired,
3240
onToggle: React.PropTypes.func.isRequired,
3341
sortKey: React.PropTypes.string,
3442
task: React.PropTypes.object.isRequired,
@@ -37,28 +45,31 @@ var TaskListItemComponent = React.createClass({
3745

3846
getHostAndPorts: function () {
3947
var task = this.props.task;
48+
var props = this.props;
4049
var ports = task.ports;
4150

4251
if (ports == null || ports.length === 0 ) {
4352
return (<span className="text-muted">{task.host}</span>);
4453
}
4554

4655
if (ports != null && ports.length === 1) {
56+
const scheme = getPortScheme(props.labels, 0);
4757
return (
4858
<a className="text-muted"
49-
href={`//${task.host}:${ports[0]}`}
59+
href={`${scheme}${task.host}:${ports[0]}`}
5060
target="_blank">
5161
{`${task.host}:${ports[0]}`}
5262
</a>
5363
);
5464
}
5565

5666
if (ports != null && ports.length > 1) {
57-
let portNodes = ports.map(function (port) {
67+
let portNodes = ports.map(function (port, i) {
68+
const scheme = getPortScheme(props.labels, i);
5869
return (
5970
<a key={`${task.host}:${port}`}
6071
className="text-muted"
61-
href={`//${task.host}:${port}`}
72+
href={`${scheme}${task.host}:${port}`}
6273
target="_blank">
6374
{port}
6475
</a>
@@ -98,18 +109,22 @@ var TaskListItemComponent = React.createClass({
98109
let ipAddress = address.ipAddress;
99110
if (serviceDiscoveryPorts.length === 1) {
100111
let port = serviceDiscoveryPorts[0].number;
112+
const scheme = getPortScheme(props.labels, 0);
101113
return (
102114
<a key={`${ipAddress}:${port}`}
103-
className="text-muted" href={`//${ipAddress}:${port}`}>
115+
className="text-muted"
116+
href={`${scheme}${ipAddress}:${port}`}>
104117
{`${ipAddress}:${port}`}
105118
</a>
106119
);
107120
}
108121

109-
let portNodes = serviceDiscoveryPorts.map((port) => {
122+
let portNodes = serviceDiscoveryPorts.map((port, i) => {
123+
const scheme = getPortScheme(props.labels, i);
110124
return (
111125
<a key={`${ipAddress}:${port.number}`}
112-
className="text-muted" href={`//${ipAddress}:${port.number}`}>
126+
className="text-muted"
127+
href={`${scheme}${ipAddress}:${port.number}`}>
113128
{port.number}
114129
</a>
115130
);

src/js/components/TaskViewComponent.jsx

Lines changed: 2 additions & 0 deletions
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

Lines changed: 36 additions & 0 deletions
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;
Lines changed: 56 additions & 0 deletions
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

Lines changed: 72 additions & 1 deletion
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)