1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-07 09:44:23 +03:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Vincent Demeester
adca5dc55b Merge pull request #52 from EmileVauge/emilevauge-add-traefik-domain
Add traefik.domain label #51
2015-10-09 23:40:31 +02:00
Emile Vauge
641638ba3e Merge branch 'master' into emilevauge-add-traefik-domain 2015-10-09 20:33:25 +02:00
Vincent Demeester
fb7457eba0 Merge pull request #54 from EmileVauge/better-doc-on-kv-structure
Update of the doc about kv
2015-10-09 14:13:03 +02:00
emile
ddf1922eba Update doc about kv structure https://github.com/EmileVauge/traefik/issues/40 2015-10-09 10:34:56 +02:00
Emile Vauge
13f621a9ed Merge branch 'master' into emilevauge-add-traefik-domain 2015-10-08 22:56:34 +02:00
Vincent Demeester
74c5562c2b Merge pull request #43 from ldez/feature/health-graph
Health graph
2015-10-08 22:55:41 +02:00
Fernandez Ludovic
09320b99f9 docs(webui): update screenshots of the web ui 2015-10-08 22:32:39 +02:00
Fernandez Ludovic
a422f775e6 refactor(webui): homepage link correction 2015-10-08 22:32:39 +02:00
Fernandez Ludovic
facc936fe4 docs: enhance Health API documentation 2015-10-08 22:32:38 +02:00
Fernandez Ludovic
28458345b4 feat(webui): new Health screen
- add realtime chart
  - Total Status Code Count
  - Average response time
  - remove status code count
  - D3 & NVD3 & Angular NVD3
2015-10-08 22:32:38 +02:00
Emile Vauge
398dfbd8a5 Merge pull request #53 from EmileVauge/emilevauge-sleep-well
Sleep well
2015-10-08 22:30:56 +02:00
Emile Vauge
3a877a51b9 Merge branch 'master' into emilevauge-sleep-well 2015-10-08 22:21:29 +02:00
emile
5a979b3dd6 Changed sleep to 500ms in tests 2015-10-08 22:11:34 +02:00
Vincent Demeester
f89b727ad1 Merge pull request #47 from ldez/feature/api-frontend-routes
feat(api): add api routes for Frontend Routes
2015-10-08 21:55:12 +02:00
Fernandez Ludovic
781c6aaafa feat(api): add api routes for Frontend Routes
- add `/api/providers/{provider}/frontends/{frontend}/routes`
- add `/api/providers/{provider}/frontends/{frontend}/routes/{route}`
2015-10-08 21:44:29 +02:00
emile
f126e7585d Doc update with traefik.domain label 2015-10-08 21:25:13 +02:00
emile
27eae04e87 Added traefik.domain label. Corrects https://github.com/EmileVauge/traefik/issues/50 2015-10-08 21:21:51 +02:00
26 changed files with 563 additions and 277 deletions

View File

@@ -72,7 +72,8 @@ Refer to the [benchmarks section](docs/index.md#benchmarks) in the documentation
You can access to a simple HTML frontend of Træfik.
![HTML frontend](docs/img/web.frontend.png)
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Contributing

View File

@@ -22,40 +22,6 @@ type DockerProvider struct {
Domain string
}
var DockerFuncMap = template.FuncMap{
"getBackend": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.backend" {
return value
}
}
return getHost(container)
},
"getPort": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.port" {
return value
}
}
for key := range container.NetworkSettings.Ports {
return key.Port()
}
return ""
},
"getWeight": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
"getHost": getHost,
}
func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) error {
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
log.Errorf("Failed to create a client for docker, error: %s", err)
@@ -105,6 +71,47 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage)
}
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
var DockerFuncMap = template.FuncMap{
"getBackend": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.backend" {
return value
}
}
return getHost(container)
},
"getPort": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.port" {
return value
}
}
for key := range container.NetworkSettings.Ports {
return key.Port()
}
return ""
},
"getWeight": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"getDomain": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.domain" {
return value
}
}
return provider.Domain
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
"getHost": getHost,
}
configuration := new(Configuration)
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
containersInspected := []docker.Container{}

BIN
docs/img/traefik-health.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -232,26 +232,46 @@ address = ":8080"
* `/`: provides a simple HTML frontend of Træfik
![HTML frontend](img/web.frontend.png)
![Web UI Providers](img/web.frontend.png)
![Web UI Health](img/traefik-health.png)
* `/health`: `GET` json metrics
```sh
$ curl -s "http://localhost:8080/health" | jq .
{
"average_response_time_sec": 0,
"average_response_time": "0",
"total_response_time_sec": 0,
"total_response_time": "0",
"total_count": 0,
"pid": 12861,
"uptime": "7m12.80607635s",
"uptime_sec": 432.80607635,
"time": "2015-09-22 10:25:16.448023473 +0200 CEST",
"unixtime": 1442910316,
"status_code_count": {},
"total_status_code_count": {},
"count": 0
// Træfɪk PID
"pid": 2458,
// Træfɪk server uptime (formated time)
"uptime": "39m6.885931127s",
// Træfɪk server uptime in seconds
"uptime_sec": 2346.885931127,
// current server date
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
// current server date in seconds
"unixtime": 1444235544,
// count HTTP response status code in realtime
"status_code_count": {
"502": 1
},
// count HTTP response status code since Træfɪk started
"total_status_code_count": {
"200": 7,
"404": 21,
"502": 13
},
// count HTTP response
"count": 1,
// count HTTP response
"total_count": 41,
// sum of all response time (formated time)
"total_response_time": "35.456865605s",
// sum of all response time in seconds
"total_response_time_sec": 35.456865605,
// average response time (formated time)
"average_response_time": "864.8016ms",
// average response time in seconds
"average_response_time_sec": 0.8648016000000001
}
```
@@ -321,14 +341,16 @@ $ curl -s "http://localhost:8080/api" | jq .
}
```
* `/api/providers`: `GET` providers
* `/api/providers/{provider}`: `GET` or `PUT` provider
* `/api/providers/{provider}/backends`: `GET` backends
* `/api/providers/{provider}/backends/{backend}`: `GET` a backend
* `/api/providers/{provider}/backends/{backend}/servers`: `GET` servers in a backend
* `/api/providers/{provider}/backends/{backend}/servers/{server}`: `GET` a server in a backend
* `/api/providers/{provider}/frontends`: `GET` frontends
* `/api/providers/{provider}/frontends/{frontend}`: `GET` a frontend
- `/api/providers`: `GET` providers
- `/api/providers/{provider}`: `GET` or `PUT` provider
- `/api/providers/{provider}/backends`: `GET` backends
- `/api/providers/{provider}/backends/{backend}`: `GET` a backend
- `/api/providers/{provider}/backends/{backend}/servers`: `GET` servers in a backend
- `/api/providers/{provider}/backends/{backend}/servers/{server}`: `GET` a server in a backend
- `/api/providers/{provider}/frontends`: `GET` frontends
- `/api/providers/{provider}/frontends/{frontend}`: `GET` a frontend
- `/api/providers/{provider}/frontends/{frontend}/routes`: `GET` routes in a frontend
- `/api/providers/{provider}/frontends/{frontend}/routes/{route}`: `GET` a route in a frontend
## <a id="docker"></a> Docker backend
@@ -380,6 +402,7 @@ Labels can be used on containers to override default behaviour:
* `traefik.weight=10`: assign this weight to the container
* `traefik.enable=false`: disable this container in Træfɪk
* `traefik.host=bar`: override the default routing from {containerName}.{domain} to bar.{domain}
* `traefik.domain=traefik.localhost`: override the default domain
## <a id="marathon"></a> Marathon backend
@@ -439,6 +462,7 @@ Labels can be used on containers to override default behaviour:
* `traefik.enable=false`: disable this application in Træfɪk
* `traefik.host=bar`: override the default routing from {appName}.{domain} to bar.{domain}
* `traefik.prefixes=pf1,pf2`: use PathPrefix(es) instead of hostname for routing, use filename="providerTemplates/marathon-prefix.tmpl" with this option
* `traefik.domain=traefik.localhost`: override the default domain
## <a id="consul"></a> Consul backend
@@ -480,6 +504,45 @@ prefix = "traefik"
# filename = "consul.tmpl"
```
The Keys-Values structure should look (using `prefix = "/traefik"`):
- backend 1
| Key | Value |
| ------------- | ----------- |
| /traefik/backends/backend1/circuitbreaker/expression | `NetworkErrorRatio() > 0.5` |
| /traefik/backends/backend1/servers/server1/url | `http://172.17.0.2:80` |
| /traefik/backends/backend1/servers/server1/weight | `10` |
| /traefik/backends/backend1/servers/server2/url | `http://172.17.0.3:80` |
| /traefik/backends/backend1/servers/server2/weight | `1` |
- backend 2
| Key | Value |
| ------------- | ----------- |
| /traefik/backends/backend2/loadbalancer/method | `drr` |
| /traefik/backends/backend2/servers/server1/url | `http://172.17.0.4:80` |
| /traefik/backends/backend2/servers/server1/weight | `1` |
| /traefik/backends/backend2/servers/server2/url | `http://172.17.0.5:80` |
| /traefik/backends/backend2/servers/server2/weight | `2` |
- frontend 1
| Key | Value |
| ------------- | ----------- |
| /traefik/frontends/frontend1/backend | `backend2` |
| /traefik/frontends/frontend1/routes/test_1/rule | `Host` |
| /traefik/frontends/frontend1/routes/test_1/value | `test.localhost` |
- frontend 2
| Key | Value |
| ------------- | ----------- |
| /traefik/frontends/frontend2/backend | `backend1` |
| /traefik/frontends/frontend2/routes/test_2/rule | `Path` |
| /traefik/frontends/frontend2/routes/test_2/value | `/test` |
## <a id="etcd"></a> Etcd backend
Træfɪk can be configured to use Etcd as a backend configuration:
@@ -520,6 +583,45 @@ Træfɪk can be configured to use Etcd as a backend configuration:
# filename = "etcd.tmpl"
```
The Keys-Values structure should look (using `prefix = "/traefik"`):
- backend 1
| Key | Value |
| ------------- | ----------- |
| /traefik/backends/backend1/circuitbreaker/expression | `NetworkErrorRatio() > 0.5` |
| /traefik/backends/backend1/servers/server1/url | `http://172.17.0.2:80` |
| /traefik/backends/backend1/servers/server1/weight | `10` |
| /traefik/backends/backend1/servers/server2/url | `http://172.17.0.3:80` |
| /traefik/backends/backend1/servers/server2/weight | `1` |
- backend 2
| Key | Value |
| ------------- | ----------- |
| /traefik/backends/backend2/loadbalancer/method | `drr` |
| /traefik/backends/backend2/servers/server1/url | `http://172.17.0.4:80` |
| /traefik/backends/backend2/servers/server1/weight | `1` |
| /traefik/backends/backend2/servers/server2/url | `http://172.17.0.5:80` |
| /traefik/backends/backend2/servers/server2/weight | `2` |
- frontend 1
| Key | Value |
| ------------- | ----------- |
| /traefik/frontends/frontend1/backend | `backend2` |
| /traefik/frontends/frontend1/routes/test_1/rule | `Host` |
| /traefik/frontends/frontend1/routes/test_1/value | `test.localhost` |
- frontend 2
| Key | Value |
| ------------- | ----------- |
| /traefik/frontends/frontend2/backend | `backend1` |
| /traefik/frontends/frontend2/routes/test_2/rule | `Path` |
| /traefik/frontends/frontend2/routes/test_2/value | `/test` |
## <a id="zk"></a> Zookeeper backend
Træfɪk can be configured to use Zookeeper as a backend configuration:
@@ -559,6 +661,44 @@ Træfɪk can be configured to use Zookeeper as a backend configuration:
#
# filename = "zookeeper.tmpl"
```
The Keys-Values structure should look (using `prefix = "/traefik"`):
- backend 1
| Key | Value |
| ------------- | ----------- |
| /traefik/backends/backend1/circuitbreaker/expression | `NetworkErrorRatio() > 0.5` |
| /traefik/backends/backend1/servers/server1/url | `http://172.17.0.2:80` |
| /traefik/backends/backend1/servers/server1/weight | `10` |
| /traefik/backends/backend1/servers/server2/url | `http://172.17.0.3:80` |
| /traefik/backends/backend1/servers/server2/weight | `1` |
- backend 2
| Key | Value |
| ------------- | ----------- |
| /traefik/backends/backend2/loadbalancer/method | `drr` |
| /traefik/backends/backend2/servers/server1/url | `http://172.17.0.4:80` |
| /traefik/backends/backend2/servers/server1/weight | `1` |
| /traefik/backends/backend2/servers/server2/url | `http://172.17.0.5:80` |
| /traefik/backends/backend2/servers/server2/weight | `2` |
- frontend 1
| Key | Value |
| ------------- | ----------- |
| /traefik/frontends/frontend1/backend | `backend2` |
| /traefik/frontends/frontend1/routes/test_1/rule | `Host` |
| /traefik/frontends/frontend1/routes/test_1/value | `test.localhost` |
- frontend 2
| Key | Value |
| ------------- | ----------- |
| /traefik/frontends/frontend2/backend | `backend1` |
| /traefik/frontends/frontend2/routes/test_2/rule | `Path` |
| /traefik/frontends/frontend2/routes/test_2/value | `/test` |
## <a id="boltdb"></a> BoltDB backend

View File

@@ -38,7 +38,7 @@ func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) {
err := cmd.Start()
c.Assert(err, checker.IsNil)
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
// TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1/")

View File

@@ -14,7 +14,7 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) {
err := cmd.Start()
c.Assert(err, checker.IsNil)
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
// TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1/")

View File

@@ -18,7 +18,7 @@ func (s *DockerSuite) TestSimpleConfiguration(c *check.C) {
err := cmd.Start()
c.Assert(err, checker.IsNil)
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
// TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1/")

View File

@@ -14,7 +14,7 @@ func (s *FileSuite) TestSimpleConfiguration(c *check.C) {
err := cmd.Start()
c.Assert(err, checker.IsNil)
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
// TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1/")

View File

@@ -14,7 +14,7 @@ func (s *MarathonSuite) TestSimpleConfiguration(c *check.C) {
err := cmd.Start()
c.Assert(err, checker.IsNil)
time.Sleep(100 * time.Millisecond)
time.Sleep(500 * time.Millisecond)
// TODO validate : run on 80
resp, err := http.Get("http://127.0.0.1/")

View File

@@ -21,42 +21,6 @@ type MarathonProvider struct {
NetworkInterface string
}
var MarathonFuncMap = template.FuncMap{
"getPort": func(task marathon.Task) string {
for _, port := range task.Ports {
return strconv.Itoa(port)
}
return ""
},
"getHost": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.host" {
return value
}
}
return strings.Replace(application.ID, "/", "", 1)
},
"getWeight": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"getPrefixes": func(application marathon.Application) ([]string, error) {
for key, value := range application.Labels {
if key == "traefik.prefixes" {
return strings.Split(value, ","), nil
}
}
return []string{}, nil
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
}
func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) error {
config := marathon.NewDefaultConfig()
config.URL = provider.Endpoint
@@ -91,6 +55,49 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage
}
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
var MarathonFuncMap = template.FuncMap{
"getPort": func(task marathon.Task) string {
for _, port := range task.Ports {
return strconv.Itoa(port)
}
return ""
},
"getHost": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.host" {
return value
}
}
return strings.Replace(application.ID, "/", "", 1)
},
"getWeight": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"getDomain": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.domain" {
return value
}
}
return provider.Domain
},
"getPrefixes": func(application marathon.Application) ([]string, error) {
for key, value := range application.Labels {
if key == "traefik.prefixes" {
return strings.Split(value, ","), nil
}
}
return []string{}, nil
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
}
configuration := new(Configuration)
applications, err := provider.marathonClient.Applications(nil)

View File

@@ -10,5 +10,5 @@
backend = "backend-{{getBackend $container}}"
[frontends.frontend-{{$host}}.routes.route-host-{{$host}}]
rule = "Host"
value = "{{$host}}.{{$.Domain}}"
value = "{{$host}}.{{getDomain $container}}"
{{end}}

View File

@@ -22,6 +22,6 @@
backend = "backend{{.ID | replace "/" "-"}}"
[frontends.frontend-{{getHost $app | replace "/" "-"}}.routes.route-host-{{getHost $app | replace "/" "-"}}]
rule = "Host"
value = "{{getHost $app | replace "/" "-"}}.{{$.Domain}}"
value = "{{getHost $app | replace "/" "-"}}.{{getDomain .}}"
{{end}}
{{end}}

View File

@@ -15,5 +15,5 @@
backend = "backend{{.ID | replace "/" "-"}}"
[frontends.frontend-{{getHost . | replace "/" "-"}}.routes.route-host-{{getHost . | replace "/" "-"}}]
rule = "Host"
value = "{{getHost . | replace "/" "-"}}.{{$.Domain}}"
value = "{{getHost . | replace "/" "-"}}.{{getDomain .}}"
{{end}}

View File

@@ -6,21 +6,199 @@
var vm = this;
vm.health = Health.get();
vm.graph = {
averageResponseTime: {},
totalStatusCodeCount: {}
}
vm.graph.totalStatusCodeCount.options = {
"chart": {
type: 'discreteBarChart',
height: 200,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 55
},
x: function (d) {
return d.label;
},
y: function (d) {
return d.value;
},
showValues: true,
valueFormat: function (d) {
return d3.format('d')(d);
},
transitionDuration: 50,
yAxis: {
axisLabelDistance: 30
}
},
"title": {
"enable": true,
"text": "Total Status Code Count",
"css": {
"textAlign": "center"
}
}
};
vm.graph.totalStatusCodeCount.data = [
{
key: "Total Status Code Count",
values: [
{
"label": "200",
"value": 0
}
]
}
];
/**
* Update Total Status Code Count graph
*
* @param {Object} totalStatusCodeCount Object from API
*/
function updateTotalStatusCodeCount(totalStatusCodeCount) {
// extract values
vm.graph.totalStatusCodeCount.data[0].values = [];
for (var code in totalStatusCodeCount) {
if (totalStatusCodeCount.hasOwnProperty(code)) {
vm.graph.totalStatusCodeCount.data[0].values.push({
label: code,
value: totalStatusCodeCount[code]
})
}
}
// Update Total Status Code Count graph render
if (vm.graph.totalStatusCodeCount.api) {
vm.graph.totalStatusCodeCount.api.update();
} else {
console.error('fail');
}
}
vm.graph.averageResponseTime.options = {
chart: {
type: 'lineChart',
height: 200,
margin: {
top: 20,
right: 40,
bottom: 40,
left: 55
},
transitionDuration: 50,
x: function (d) {
return d.x;
},
y: function (d) {
return d.y;
},
useInteractiveGuideline: true,
xAxis: {
tickFormat: function (d) {
return d3.time.format('%X')(new Date(d));
}
},
yAxis: {
tickFormat: function (d) {
return d3.format(',.1f')(d);
}
}
},
"title": {
"enable": true,
"text": "Average response time",
"css": {
"textAlign": "center"
}
}
};
var initialPoint = {
x: Date.now() - 3000,
y: 0
};
vm.graph.averageResponseTime.data = [
{
values: [initialPoint],
key: 'Average response time (ms)',
type: 'line',
color: '#2ca02c'
}
];
/**
* Update average response time graph
*
* @param {Number} x Coordinate X
* @param {Number} y Coordinate Y
*/
function updateAverageResponseTimeGraph(x, y) {
// x multiply 1000 by because unix time is in seconds and JS Date are in milliseconds
var data = {
x: x * 1000,
y: y * 1000
};
vm.graph.averageResponseTime.data[0].values.push(data);
// limit graph entries
if (vm.graph.averageResponseTime.data[0].values.length > 100) {
vm.graph.averageResponseTime.data[0].values.shift();
}
// Update Average Response Time graph render
if (vm.graph.averageResponseTime.api) {
vm.graph.averageResponseTime.api.update();
}
}
/**
* Load all graph's datas
*
* @param {Object} health Health data from server
*/
function loadData(health) {
// Load datas and update Average Response Time graph render
updateAverageResponseTimeGraph(health.unixtime, health.average_response_time_sec);
// Load datas and update Total Status Code Count graph render
updateTotalStatusCodeCount(health.total_status_code_count);
// set data's view
vm.health = health;
}
/**
* Action when load datas failed
*
* @param {Object} error Error state object
*/
function erroData(error) {
vm.health = {};
$log.error(error);
}
// first load
Health.get(loadData, erroData);
// Auto refresh data
var intervalId = $interval(function () {
Health.get(function (health) {
vm.health = health;
}, function (error) {
vm.health = {};
$log.error(error);
});
Health.get(loadData, erroData);
}, 3000);
// Stop auto refresh when page change
$scope.$on('$destroy', function () {
$interval.cancel(intervalId);
});
}]);
}]);
})();

View File

@@ -2,45 +2,42 @@
<h1 class="text-danger">
<span class="glyphicon glyphicon-heart" aria-hidden="true"></span> Health
</h1>
<ul class="list-group">
<li class="list-group-item">
<span>Average response time sec :</span><span class="badge">{{healthCtrl.health.average_response_time_sec}}</span>
</li>
<li class="list-group-item">
<span>Average response time :</span><span class="badge">{{healthCtrl.health.average_response_time}}</span>
</li>
<li class="list-group-item">
<span>Total response time sec :</span><span class="badge">{{healthCtrl.health.total_response_time_sec}}</span>
</li>
<li class="list-group-item">
<span>Total response time :</span><span class="badge">{{healthCtrl.health.total_response_time}}</span>
</li>
<li class="list-group-item">
<span>Total count :</span><span class="badge">{{healthCtrl.health.total_count}}</span>
</li>
<li class="list-group-item">
<span>PID :</span><span class="badge">{{healthCtrl.health.pid}}</span>
</li>
<li class="list-group-item">
<span>Uptime :</span><span class="badge">{{healthCtrl.health.uptime}}</span>
</li>
<li class="list-group-item">
<span>Uptime sec :</span><span class="badge">{{healthCtrl.health.uptime_sec}}</span>
</li>
<li class="list-group-item">
<span>Time :</span><span class="badge">{{healthCtrl.health.time}}</span>
</li>
<li class="list-group-item">
<span>Unixtime :</span><span class="badge">{{healthCtrl.health.unixtime}}</span>
</li>
<li class="list-group-item">
<span>Status code count :</span><span class="badge">{{healthCtrl.health.status_code_count}}</span>
</li>
<li class="list-group-item">
<span>Total status code count :</span><span class="badge">{{healthCtrl.health.total_status_code_count}}</span>
</li>
<li class="list-group-item">
<span>Count :</span><span class="badge">{{healthCtrl.health.count}}</span>
</li>
</ul>
<div class="row">
<div class="col-md-6">
<div>
<nvd3 options="healthCtrl.graph.averageResponseTime.options" data="healthCtrl.graph.averageResponseTime.data" api="healthCtrl.graph.averageResponseTime.api"></nvd3>
</div>
<ul class="list-group">
<li class="list-group-item">
<span>Total response time :</span><span class="badge">{{healthCtrl.health.total_response_time}}</span>
</li>
</ul>
<ul class="list-group">
<li class="list-group-item">
<span>PID :</span><span class="badge">{{healthCtrl.health.pid}}</span>
</li>
<li class="list-group-item">
<span>Uptime :</span><span class="badge">{{healthCtrl.health.uptime}}</span>
</li>
</ul>
</div>
<div class="col-md-6">
<div>
<nvd3 options="healthCtrl.graph.totalStatusCodeCount.options" data="healthCtrl.graph.totalStatusCodeCount.data" api="healthCtrl.graph.totalStatusCodeCount.api"></nvd3>
</div>
<ul class="list-group">
<li class="list-group-item">
<span>Total count :</span><span class="badge">{{healthCtrl.health.total_count}}</span>
</li>
<li class="list-group-item">
<span>Count :</span><span class="badge">{{healthCtrl.health.count}}</span>
</li>
</ul>
</div>
</div>
</div>

View File

@@ -5,6 +5,7 @@
.module('traefik.section', [
'ui.router',
'ui.bootstrap',
'nvd3',
'traefik.section.providers',
'traefik.section.health'
]);

View File

@@ -1,7 +1,7 @@
{
"name": "traefik",
"version": "1.0.0",
"homepage": "https://github.com/ldez/traefik",
"homepage": "http://traefik.io",
"authors": [
"Fernandez Ludovic <ludovic.fernandez@zenika.com>"
],
@@ -20,6 +20,7 @@
"bootstrap": "~3.3.5",
"angular-resource": "~1.4.7",
"angular-ui-router": "~0.2.15",
"angular-bootstrap": "~0.13.4"
"angular-bootstrap": "~0.13.4",
"angular-nvd3": "~1.0.2"
}
}

File diff suppressed because one or more lines are too long

5
static/bower_components/d3/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,6 +13,9 @@
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="bower_components/angular-bootstrap/ui-bootstrap-csp.css">
<!-- NVD3 -->
<link rel="stylesheet" href="bower_components/nvd3/build/nv.d3.min.css">
<link rel="stylesheet" href="style.css">
</head>
@@ -57,6 +60,9 @@
<script src="bower_components/angular-resource/angular-resource.min.js"></script>
<script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
<script src="bower_components/d3/d3.min.js"></script>
<script src="bower_components/nvd3/build/nv.d3.min.js"></script>
<script src="bower_components/angular-nvd3/dist/angular-nvd3.min.js"></script>
<!-- end vendors -->
<script src="app/traefik.js"></script>

View File

@@ -26,4 +26,7 @@ Do `npm install` and `bower install`
- [UI Router - Documentation](https://github.com/angular-ui/ui-router/wiki)
- [Bootstrap](http://getbootstrap.com)
- [Angular Bootstrap](https://angular-ui.github.io/bootstrap)
- [D3](http://d3js.org)
- [D3 - Documentation](https://github.com/mbostock/d3/wiki)
- [NVD3](http://nvd3.org)
- [Angular nvD3](http://krispo.github.io/angular-nvd3)

View File

@@ -1,101 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>/ˈTræfɪk/</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap-theme.min.css">
<script src="/static/jquery-2.1.4.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<style type="text/css">
body {padding-bottom: 70px;}
.content {margin:10px;}
</style>
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>/ˈTræfɪk/</h1>
</div>
<div class="row">
<div class="col-md-6">
<!-- <div class="panel-heading">Frontends</div>
<div class="panel-body"> -->
{{range $keyProviders, $valueProviders := .Configurations}}
{{range $keyFrontends, $valueFrontends := $valueProviders.Frontends}}
<div class="panel panel-primary">
<div class="panel-heading">{{$keyFrontends}} - ({{$keyProviders}})</div>
<div class="panel-body">
<a class="btn btn-info" role="button" data-toggle="collapse" href="#{{$valueFrontends.Backend}}" aria-expanded="false">
{{$valueFrontends.Backend}}
</a>
</div>
<table class="table table-striped table-hover">
<tr>
<td><em>Route</em></td>
<td><em>Rule</em></td>
<td><em>Value</em></td>
</tr>
{{range $keyRoutes, $valueRoutes := $valueFrontends.Routes}}
<tr>
<td>{{$keyRoutes}}</td>
<td>{{$valueRoutes.Rule}}</td>
<td>{{$valueRoutes.Value}}</td>
</tr>
{{end}}
</table>
</div>
{{end}}
{{end}}
<!-- </div> -->
</div>
<div class="col-md-6">
<!-- <div class="panel-heading">Backends</div>
<div class="panel-body"> -->
{{range $keyProviders, $valueProviders := .Configurations}}
{{range $keyBackends, $valueBackends := $valueProviders.Backends}}
<div class="panel panel-primary" id="{{$keyBackends}}">
<div class="panel-heading">{{$keyBackends}} - ({{$keyProviders}})</div>
<div class="panel-body">
{{with $valueBackends.LoadBalancer}}
<a class="btn btn-info" role="button">
Load Balancer: {{.Method}}
</a>
{{end}}
{{with $valueBackends.CircuitBreaker}}
<a class="btn btn-info" role="button">
Circuit Breaker: {{.Expression}}
</a>
{{end}}
</div>
<table class="table table-striped table-hover">
<tr>
<td><em>Server</em></td>
<td><em>URL</em></td>
<td><em>Weight</em></td>
</tr>
{{range $keyServers, $valueServers := $valueBackends.Servers}}
<tr>
<td>{{$keyServers}}</td>
<td><code><A href="{{$valueServers.URL}}">{{$valueServers.URL}}</A></code></td>
<td>{{$valueServers.Weight}}</td>
</tr>
{{end}}
</table>
</div>
{{end}}
{{end}}
<!-- </div> -->
</div>
</div>
</div>
</body>
</html>

77
web.go
View File

@@ -51,6 +51,8 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err
systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers/{server}").HandlerFunc(getServerHandler)
systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends").HandlerFunc(getFrontendsHandler)
systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}").HandlerFunc(getFrontendHandler)
systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}/routes").HandlerFunc(getRoutesHandler)
systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}/routes/{route}").HandlerFunc(getRouteHandler)
// Expose dashboard
systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
@@ -115,29 +117,6 @@ func getBackendHandler(response http.ResponseWriter, request *http.Request) {
http.NotFound(response, request)
}
func getFrontendsHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
if provider, ok := currentConfigurations[providerID]; ok {
templatesRenderer.JSON(response, http.StatusOK, provider.Frontends)
} else {
http.NotFound(response, request)
}
}
func getFrontendHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
frontendID := vars["frontend"]
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
templatesRenderer.JSON(response, http.StatusOK, frontend)
return
}
}
http.NotFound(response, request)
}
func getServersHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
@@ -166,3 +145,55 @@ func getServerHandler(response http.ResponseWriter, request *http.Request) {
}
http.NotFound(response, request)
}
func getFrontendsHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
if provider, ok := currentConfigurations[providerID]; ok {
templatesRenderer.JSON(response, http.StatusOK, provider.Frontends)
} else {
http.NotFound(response, request)
}
}
func getFrontendHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
frontendID := vars["frontend"]
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
templatesRenderer.JSON(response, http.StatusOK, frontend)
return
}
}
http.NotFound(response, request)
}
func getRoutesHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
frontendID := vars["frontend"]
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
templatesRenderer.JSON(response, http.StatusOK, frontend.Routes)
return
}
}
http.NotFound(response, request)
}
func getRouteHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := vars["provider"]
frontendID := vars["frontend"]
routeID := vars["route"]
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
if route, ok := frontend.Routes[routeID]; ok {
templatesRenderer.JSON(response, http.StatusOK, route)
return
}
}
}
http.NotFound(response, request)
}