mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #246 from philips/add-dashboard-0.2
0.2: add the dashboard
This commit is contained in:
commit
fc25ef8383
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,5 @@ src/
|
|||||||
pkg/
|
pkg/
|
||||||
/etcd
|
/etcd
|
||||||
/server/release_version.go
|
/server/release_version.go
|
||||||
|
/go-bindata
|
||||||
/machine*
|
/machine*
|
||||||
|
20
.header
Normal file
20
.header
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package x
|
||||||
|
|
||||||
|
import (
|
||||||
|
)
|
30
README.md
30
README.md
@ -416,8 +416,8 @@ A similar argument `-sl` is used to setup the listening address for the server p
|
|||||||
Let the join two more nodes to this cluster using the -C argument:
|
Let the join two more nodes to this cluster using the -C argument:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./etcd -c 127.0.0.1:4002 -s 127.0.0.1:7002 -C 127.0.0.1:7001 -d nodes/node2 -n node2
|
./etcd -s 127.0.0.1:7002 -c 127.0.0.1:4002 -C 127.0.0.1:7001 -d nodes/node2 -n node2
|
||||||
./etcd -c 127.0.0.1:4003 -s 127.0.0.1:7003 -C 127.0.0.1:7001 -d nodes/node3 -n node3
|
./etcd -s 127.0.0.1:7003 -c 127.0.0.1:4003 -C 127.0.0.1:7001 -d nodes/node3 -n node3
|
||||||
```
|
```
|
||||||
|
|
||||||
Get the machines in the cluster:
|
Get the machines in the cluster:
|
||||||
@ -557,6 +557,10 @@ See [CONTRIBUTING](https://github.com/coreos/etcd/blob/master/CONTRIBUTING.md) f
|
|||||||
|
|
||||||
- [jdarcy/etcd-api](https://github.com/jdarcy/etcd-api)
|
- [jdarcy/etcd-api](https://github.com/jdarcy/etcd-api)
|
||||||
|
|
||||||
|
**Clojure libraries**
|
||||||
|
|
||||||
|
- [aterreno/etcd-clojure](https://github.com/aterreno/etcd-clojure)
|
||||||
|
|
||||||
**Chef Integration**
|
**Chef Integration**
|
||||||
|
|
||||||
- [coderanger/etcd-chef](https://github.com/coderanger/etcd-chef)
|
- [coderanger/etcd-chef](https://github.com/coderanger/etcd-chef)
|
||||||
@ -570,10 +574,12 @@ See [CONTRIBUTING](https://github.com/coreos/etcd/blob/master/CONTRIBUTING.md) f
|
|||||||
- [binocarlos/yoda](https://github.com/binocarlos/yoda) - etcd + ZeroMQ
|
- [binocarlos/yoda](https://github.com/binocarlos/yoda) - etcd + ZeroMQ
|
||||||
- [calavera/active-proxy](https://github.com/calavera/active-proxy) - HTTP Proxy configured with etcd
|
- [calavera/active-proxy](https://github.com/calavera/active-proxy) - HTTP Proxy configured with etcd
|
||||||
- [derekchiang/etcdplus](https://github.com/derekchiang/etcdplus) - A set of distributed synchronization primitives built upon etcd
|
- [derekchiang/etcdplus](https://github.com/derekchiang/etcdplus) - A set of distributed synchronization primitives built upon etcd
|
||||||
|
- [go-discover](https://github.com/flynn/go-discover) - service discovery in Go
|
||||||
- [gleicon/goreman](https://github.com/gleicon/goreman/tree/etcd) - Branch of the Go Foreman clone with etcd support
|
- [gleicon/goreman](https://github.com/gleicon/goreman/tree/etcd) - Branch of the Go Foreman clone with etcd support
|
||||||
- [garethr/hiera-etcd](https://github.com/garethr/hiera-etcd) - Puppet hiera backend using etcd
|
- [garethr/hiera-etcd](https://github.com/garethr/hiera-etcd) - Puppet hiera backend using etcd
|
||||||
- [mattn/etcd-vim](https://github.com/mattn/etcd-vim) - SET and GET keys from inside vim
|
- [mattn/etcd-vim](https://github.com/mattn/etcd-vim) - SET and GET keys from inside vim
|
||||||
- [mattn/etcdenv](https://github.com/mattn/etcdenv) - "env" shebang with etcd integration
|
- [mattn/etcdenv](https://github.com/mattn/etcdenv) - "env" shebang with etcd integration
|
||||||
|
- [kelseyhightower/confd](https://github.com/kelseyhightower/confd) - Manage local app config files using templates and data from etcd
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
@ -588,6 +594,26 @@ Odd numbers are good because if you have 8 machines the majority will be 5 and i
|
|||||||
The result is that an 8 machine cluster can tolerate 3 machine failures and a 9 machine cluster can tolerate 4 nodes failures.
|
The result is that an 8 machine cluster can tolerate 3 machine failures and a 9 machine cluster can tolerate 4 nodes failures.
|
||||||
And in the best case when all 9 machines are responding the cluster will perform at the speed of the fastest 5 nodes.
|
And in the best case when all 9 machines are responding the cluster will perform at the speed of the fastest 5 nodes.
|
||||||
|
|
||||||
|
### Why SSLv3 alert handshake failure when using SSL client auth?
|
||||||
|
The `TLS` pacakge of `golang` checks the key usage of certificate public key before using it. To use the certificate public key to do client auth, we need to add `clientAuth` to `Extended Key Usage` when creating the certificate public key.
|
||||||
|
|
||||||
|
Here is how to do it:
|
||||||
|
|
||||||
|
Add the following section to your openssl.cnf:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ ssl_client ]
|
||||||
|
...
|
||||||
|
extendedKeyUsage = clientAuth
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
When creating the cert be sure to reference it in the -extensions flag:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/node.crt -infiles node.csr
|
||||||
|
```
|
||||||
|
|
||||||
## Project Details
|
## Project Details
|
||||||
|
|
||||||
### Versioning
|
### Versioning
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package error
|
package error
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
16
etcd.go
16
etcd.go
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
10
mod/README.md
Normal file
10
mod/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
## Etcd modules
|
||||||
|
|
||||||
|
etcd modules (mods) are higher order pieces of functionality that only
|
||||||
|
speak to the client etcd API and are presented in the `/etcd/mod` HTTP path
|
||||||
|
of the etcd service.
|
||||||
|
|
||||||
|
The basic idea is that etcd can ship things like dashboards, master
|
||||||
|
election APIs and other helpful services that would normally have to
|
||||||
|
stand up and talk to an etcd cluster directly in the binary. It is a
|
||||||
|
convienence and hopefully eases complexity in deployments.
|
3
mod/dashboard/.bowerrc
Normal file
3
mod/dashboard/.bowerrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"directory": "app/bower_components"
|
||||||
|
}
|
21
mod/dashboard/.editorconfig
Normal file
21
mod/dashboard/.editorconfig
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# EditorConfig helps developers define and maintain consistent
|
||||||
|
# coding styles between different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# Change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# We recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
1
mod/dashboard/.gitattributes
vendored
Normal file
1
mod/dashboard/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto
|
6
mod/dashboard/.gitignore
vendored
Normal file
6
mod/dashboard/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.tmp
|
||||||
|
.sass-cache
|
||||||
|
app/bower_components
|
||||||
|
/go-bindata
|
3
mod/dashboard/.jshintignore
Normal file
3
mod/dashboard/.jshintignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
app/scripts/vega.js
|
||||||
|
app/scripts/moment.min.js
|
||||||
|
app/scripts/ng-time-relative.min.js
|
27
mod/dashboard/.jshintrc
Normal file
27
mod/dashboard/.jshintrc
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"node": true,
|
||||||
|
"browser": true,
|
||||||
|
"esnext": true,
|
||||||
|
"bitwise": true,
|
||||||
|
"camelcase": true,
|
||||||
|
"curly": true,
|
||||||
|
"eqeqeq": true,
|
||||||
|
"immed": true,
|
||||||
|
"indent": 2,
|
||||||
|
"latedef": true,
|
||||||
|
"newcap": true,
|
||||||
|
"noarg": true,
|
||||||
|
"quotmark": "single",
|
||||||
|
"regexp": true,
|
||||||
|
"undef": true,
|
||||||
|
"unused": false,
|
||||||
|
"strict": true,
|
||||||
|
"trailing": true,
|
||||||
|
"smarttabs": true,
|
||||||
|
"globals": {
|
||||||
|
"angular": false,
|
||||||
|
"$": false,
|
||||||
|
"vg": false,
|
||||||
|
"moment": false
|
||||||
|
}
|
||||||
|
}
|
7
mod/dashboard/.travis.yml
Normal file
7
mod/dashboard/.travis.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- '0.8'
|
||||||
|
- '0.10'
|
||||||
|
before_script:
|
||||||
|
- 'npm install -g bower grunt-cli'
|
||||||
|
- 'bower install'
|
345
mod/dashboard/Gruntfile.js
Normal file
345
mod/dashboard/Gruntfile.js
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// Generated on 2013-10-07 using generator-webapp 0.4.3
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// # Globbing
|
||||||
|
// for performance reasons we're only matching one level down:
|
||||||
|
// 'test/spec/{,*/}*.js'
|
||||||
|
// use this if you want to recursively match all subfolders:
|
||||||
|
// 'test/spec/**/*.js'
|
||||||
|
|
||||||
|
module.exports = function (grunt) {
|
||||||
|
// show elapsed time at the end
|
||||||
|
require('time-grunt')(grunt);
|
||||||
|
// load all grunt tasks
|
||||||
|
require('load-grunt-tasks')(grunt);
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
// configurable paths
|
||||||
|
uglify: {
|
||||||
|
options: {
|
||||||
|
mangle: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yeoman: {
|
||||||
|
app: 'app',
|
||||||
|
dist: 'dist'
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
compass: {
|
||||||
|
files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
|
||||||
|
tasks: ['compass:server', 'autoprefixer']
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
|
||||||
|
tasks: ['copy:styles', 'autoprefixer']
|
||||||
|
},
|
||||||
|
livereload: {
|
||||||
|
options: {
|
||||||
|
livereload: '<%= connect.options.livereload %>'
|
||||||
|
},
|
||||||
|
files: [
|
||||||
|
'<%= yeoman.app %>/*.html',
|
||||||
|
'.tmp/styles/{,*/}*.css',
|
||||||
|
'{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
|
||||||
|
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connect: {
|
||||||
|
options: {
|
||||||
|
port: 9000,
|
||||||
|
livereload: 35729,
|
||||||
|
// change this to '0.0.0.0' to access the server from outside
|
||||||
|
hostname: 'localhost'
|
||||||
|
},
|
||||||
|
livereload: {
|
||||||
|
options: {
|
||||||
|
open: true,
|
||||||
|
base: [
|
||||||
|
'.tmp',
|
||||||
|
'<%= yeoman.app %>'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
options: {
|
||||||
|
base: [
|
||||||
|
'.tmp',
|
||||||
|
'test',
|
||||||
|
'<%= yeoman.app %>'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
open: true,
|
||||||
|
base: '<%= yeoman.dist %>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clean: {
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
dot: true,
|
||||||
|
src: [
|
||||||
|
'.tmp',
|
||||||
|
'<%= yeoman.dist %>/*',
|
||||||
|
'!<%= yeoman.dist %>/.git*'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
server: '.tmp'
|
||||||
|
},
|
||||||
|
jshint: {
|
||||||
|
options: {
|
||||||
|
jshintrc: '.jshintrc'
|
||||||
|
},
|
||||||
|
all: [
|
||||||
|
'<%= yeoman.app %>/scripts/{,*/}*.js',
|
||||||
|
'!<%= yeoman.app %>/scripts/vendor/*',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mocha: {
|
||||||
|
all: {
|
||||||
|
options: {
|
||||||
|
run: true,
|
||||||
|
urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
compass: {
|
||||||
|
options: {
|
||||||
|
sassDir: '<%= yeoman.app %>/styles',
|
||||||
|
cssDir: '.tmp/styles',
|
||||||
|
generatedImagesDir: '.tmp/images/generated',
|
||||||
|
imagesDir: '<%= yeoman.app %>/images',
|
||||||
|
javascriptsDir: '<%= yeoman.app %>/scripts',
|
||||||
|
fontsDir: '<%= yeoman.app %>/styles/fonts',
|
||||||
|
importPath: '<%= yeoman.app %>/bower_components',
|
||||||
|
httpImagesPath: '/images',
|
||||||
|
httpGeneratedImagesPath: '/images/generated',
|
||||||
|
httpFontsPath: '/styles/fonts',
|
||||||
|
relativeAssets: false,
|
||||||
|
assetCacheBuster: false
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
generatedImagesDir: '<%= yeoman.dist %>/images/generated'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
options: {
|
||||||
|
debugInfo: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoprefixer: {
|
||||||
|
options: {
|
||||||
|
browsers: ['last 1 version']
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: '.tmp/styles/',
|
||||||
|
src: '{,*/}*.css',
|
||||||
|
dest: '.tmp/styles/'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// not used since Uglify task does concat,
|
||||||
|
// but still available if needed
|
||||||
|
/*concat: {
|
||||||
|
dist: {}
|
||||||
|
},*/
|
||||||
|
requirejs: {
|
||||||
|
dist: {
|
||||||
|
// Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
|
||||||
|
options: {
|
||||||
|
// `name` and `out` is set by grunt-usemin
|
||||||
|
baseUrl: '<%= yeoman.app %>/scripts',
|
||||||
|
optimize: 'none',
|
||||||
|
// TODO: Figure out how to make sourcemaps work with grunt-usemin
|
||||||
|
// https://github.com/yeoman/grunt-usemin/issues/30
|
||||||
|
//generateSourceMaps: true,
|
||||||
|
// required to support SourceMaps
|
||||||
|
// http://requirejs.org/docs/errors.html#sourcemapcomments
|
||||||
|
preserveLicenseComments: false,
|
||||||
|
useStrict: true,
|
||||||
|
wrap: true
|
||||||
|
//uglify2: {} // https://github.com/mishoo/UglifyJS2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useminPrepare: {
|
||||||
|
options: {
|
||||||
|
dest: '<%= yeoman.dist %>'
|
||||||
|
},
|
||||||
|
html: ['<%= yeoman.app %>/**/*.html']
|
||||||
|
},
|
||||||
|
usemin: {
|
||||||
|
options: {
|
||||||
|
dirs: ['<%= yeoman.dist %>']
|
||||||
|
},
|
||||||
|
html: ['<%= yeoman.dist %>/{,*/}*.html'],
|
||||||
|
css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
|
||||||
|
},
|
||||||
|
imagemin: {
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: '<%= yeoman.app %>/images',
|
||||||
|
src: '{,*/}*.{png,jpg,jpeg}',
|
||||||
|
dest: '<%= yeoman.dist %>/images'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
svgmin: {
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: '<%= yeoman.app %>/images',
|
||||||
|
src: '{,*/}*.svg',
|
||||||
|
dest: '<%= yeoman.dist %>/images'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cssmin: {
|
||||||
|
// This task is pre-configured if you do not wish to use Usemin
|
||||||
|
// blocks for your CSS. By default, the Usemin block from your
|
||||||
|
// `index.html` will take care of minification, e.g.
|
||||||
|
//
|
||||||
|
// <!-- build:css({.tmp,app}) styles/main.css -->
|
||||||
|
//
|
||||||
|
// dist: {
|
||||||
|
// files: {
|
||||||
|
// '<%= yeoman.dist %>/styles/main.css': [
|
||||||
|
// '.tmp/styles/{,*/}*.css',
|
||||||
|
// '<%= yeoman.app %>/styles/{,*/}*.css'
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
htmlmin: {
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
/*removeCommentsFromCDATA: true,
|
||||||
|
// https://github.com/yeoman/grunt-usemin/issues/44
|
||||||
|
//collapseWhitespace: true,
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
useShortDoctype: true,
|
||||||
|
removeEmptyAttributes: true,
|
||||||
|
removeOptionalTags: true*/
|
||||||
|
},
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
src: '*.html',
|
||||||
|
dest: '<%= yeoman.dist %>'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Put files not handled in other tasks here
|
||||||
|
copy: {
|
||||||
|
dist: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
dot: true,
|
||||||
|
cwd: '<%= yeoman.app %>',
|
||||||
|
dest: '<%= yeoman.dist %>',
|
||||||
|
src: [
|
||||||
|
'*.{ico,png,txt}',
|
||||||
|
'.htaccess',
|
||||||
|
'images/{,*/}*.{webp,gif}',
|
||||||
|
'styles/fonts/{,*/}*.*',
|
||||||
|
'views/*.*',
|
||||||
|
'index.html',
|
||||||
|
'bower_components/sass-bootstrap/fonts/*.*'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
expand: true,
|
||||||
|
dot: true,
|
||||||
|
cwd: '<%= yeoman.app %>/styles',
|
||||||
|
dest: '.tmp/styles/',
|
||||||
|
src: '{,*/}*.css'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modernizr: {
|
||||||
|
devFile: '<%= yeoman.app %>/bower_components/modernizr/modernizr.js',
|
||||||
|
outputFile: '<%= yeoman.dist %>/bower_components/modernizr/modernizr.js',
|
||||||
|
files: [
|
||||||
|
'<%= yeoman.dist %>/scripts/{,*/}*.js',
|
||||||
|
'<%= yeoman.dist %>/styles/{,*/}*.css',
|
||||||
|
'!<%= yeoman.dist %>/scripts/vendor/*'
|
||||||
|
],
|
||||||
|
uglify: true
|
||||||
|
},
|
||||||
|
concurrent: {
|
||||||
|
server: [
|
||||||
|
'compass',
|
||||||
|
'copy:styles'
|
||||||
|
],
|
||||||
|
test: [
|
||||||
|
'copy:styles'
|
||||||
|
],
|
||||||
|
dist: [
|
||||||
|
'compass',
|
||||||
|
'copy:styles',
|
||||||
|
'imagemin',
|
||||||
|
'svgmin',
|
||||||
|
'htmlmin'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
bower: {
|
||||||
|
options: {
|
||||||
|
exclude: ['modernizr']
|
||||||
|
},
|
||||||
|
all: {
|
||||||
|
rjsConfig: '<%= yeoman.app %>/scripts/main.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('server', function (target) {
|
||||||
|
if (target === 'dist') {
|
||||||
|
return grunt.task.run(['build', 'connect:dist:keepalive']);
|
||||||
|
}
|
||||||
|
|
||||||
|
grunt.task.run([
|
||||||
|
'clean:server',
|
||||||
|
'concurrent:server',
|
||||||
|
'autoprefixer',
|
||||||
|
'connect:livereload',
|
||||||
|
'watch'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('test', [
|
||||||
|
'clean:server',
|
||||||
|
'concurrent:test',
|
||||||
|
'autoprefixer',
|
||||||
|
'connect:test',
|
||||||
|
'mocha'
|
||||||
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('build', [
|
||||||
|
'clean:dist',
|
||||||
|
'useminPrepare',
|
||||||
|
'concurrent:dist',
|
||||||
|
'autoprefixer',
|
||||||
|
'concat',
|
||||||
|
'cssmin',
|
||||||
|
'uglify',
|
||||||
|
'usemin',
|
||||||
|
'copy:dist'
|
||||||
|
]);
|
||||||
|
|
||||||
|
grunt.registerTask('default', [
|
||||||
|
'jshint',
|
||||||
|
'test',
|
||||||
|
'build'
|
||||||
|
]);
|
||||||
|
};
|
202
mod/dashboard/LICENSE
Normal file
202
mod/dashboard/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
27
mod/dashboard/README.md
Normal file
27
mod/dashboard/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# etcd Dashboard
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
If you'd like to contribute to the etcd dashboard mod, follow these instructions. For contributing to the rest of etcd, see the contributing document in the root of the repository.
|
||||||
|
|
||||||
|
### Install yeoman
|
||||||
|
|
||||||
|
http://yeoman.io/
|
||||||
|
|
||||||
|
### Install NPM locally
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Bower Components
|
||||||
|
|
||||||
|
```
|
||||||
|
bower install
|
||||||
|
```
|
||||||
|
|
||||||
|
### View in Browser
|
||||||
|
|
||||||
|
Run etcd like you normally would and afterward browse to:
|
||||||
|
|
||||||
|
http://localhost:4001/etcd/mod/dashboard/
|
1
mod/dashboard/app/.buildignore
Normal file
1
mod/dashboard/app/.buildignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.coffee
|
50
mod/dashboard/app/browser.html
Normal file
50
mod/dashboard/app/browser.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
|
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>etcd Browser</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
|
||||||
|
|
||||||
|
<!-- build:css(.tmp) styles/main.css -->
|
||||||
|
<link rel="stylesheet" href="styles/etcd-widgets.css">
|
||||||
|
<link href="http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,400italic,600,700,900" rel="stylesheet" type="text/css">
|
||||||
|
<link href="http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600,700" rel="stylesheet" type="text/css">
|
||||||
|
<!-- endbuild -->
|
||||||
|
</head>
|
||||||
|
<body ng-app="etcdBrowser">
|
||||||
|
<!--[if lt IE 7]>
|
||||||
|
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="bower_components/es5-shim/es5-shim.js"></script>
|
||||||
|
<script src="bower_components/json3/lib/json3.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!-- Add your site or application content here -->
|
||||||
|
<div id="etd_browser" ng-view="etcd">
|
||||||
|
</div>
|
||||||
|
<!-- build:js scripts/browser-modules.js -->
|
||||||
|
<script src="bower_components/jquery/jquery.js"></script>
|
||||||
|
<script src="bower_components/angular/angular.js"></script>
|
||||||
|
<script src="bower_components/angular-resource/angular-resource.js"></script>
|
||||||
|
<script src="bower_components/angular-route/angular-route.js"></script>
|
||||||
|
<script src="bower_components/angular-cookies/angular-cookies.js"></script>
|
||||||
|
<script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
|
||||||
|
<script src="bower_components/underscore/underscore.js"></script>
|
||||||
|
<script src="bower_components/moment/moment.js"></script>
|
||||||
|
<!-- endbuild -->
|
||||||
|
|
||||||
|
<!-- build:js({.tmp,app}) scripts/browser-scripts.js -->
|
||||||
|
<script src="scripts/ng-time-relative.min.js"></script>
|
||||||
|
<script src="scripts/common/services/etcd.js"></script>
|
||||||
|
<script src="scripts/controllers/browser.js"></script>
|
||||||
|
<!-- endbuild -->
|
||||||
|
</body>
|
||||||
|
</html>
|
134
mod/dashboard/app/index.html
Normal file
134
mod/dashboard/app/index.html
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
|
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>etcd dashboard</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<link href="http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,400italic,600,700,900" rel="stylesheet" type="text/css">
|
||||||
|
<link href="http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600,700" rel="stylesheet" type="text/css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: 30px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0px 0px 20px 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #1e6ec1;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe + iframe {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
width: 100%;
|
||||||
|
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coreos-logo {
|
||||||
|
margin: 10px auto 0 auto;
|
||||||
|
height: 30px;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coreos-logo svg {
|
||||||
|
fill: #999;
|
||||||
|
max-width: 100px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#powered-by {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333;
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 190%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>etcd Dashboard</h1>
|
||||||
|
<iframe src="stats.html" style="width: 100%; height: 400px;"></iframe>
|
||||||
|
<iframe src="browser.html" style="width: 100%; height: 400px;"></iframe>
|
||||||
|
<div id="footer">
|
||||||
|
<div id="powered-by">Powered by <a href="https://github.com/coreos/etcd">etcd</a></div>
|
||||||
|
<div id="coreos-logo">
|
||||||
|
<a href="http://coreos.com">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
preserveAspectRatio="xMinYMin" viewBox="0 0 792 306" enable-background="new 0 0 792 306" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path fill="#53A3DA" d="M136.168,45.527C76.898,45.527,28.689,93.739,28.689,153c0,59.265,48.209,107.474,107.479,107.474
|
||||||
|
c59.252,0,107.465-48.209,107.465-107.474C243.633,93.739,195.42,45.527,136.168,45.527z"/>
|
||||||
|
<path fill="#F1606D" d="M136.168,55.389c-17.283,0-31.941,27.645-37.235,66.069c-0.169,1.236-0.333,2.487-0.478,3.746
|
||||||
|
c-0.723,6.047-1.213,12.335-1.458,18.808c-0.117,2.962-0.175,5.956-0.175,8.988c0,3.029,0.058,6.029,0.175,8.985
|
||||||
|
c0.245,6.472,0.735,12.764,1.458,18.811c8.104,1.049,16.769,1.761,25.807,2.099c3.907,0.146,7.872,0.233,11.907,0.233
|
||||||
|
c4.023,0,8-0.088,11.895-0.233c9.049-0.338,17.708-1.05,25.819-2.099c0.892-0.114,1.77-0.239,2.659-0.368
|
||||||
|
c33.754-4.74,57.235-15.232,57.235-27.428C233.776,99.088,190.071,55.389,136.168,55.389z"/>
|
||||||
|
<path fill="#FFFFFF" d="M176.541,125.569c-0.979-1.428-2.029-2.796-3.148-4.11c-8.956-10.557-22.297-17.265-37.224-17.265
|
||||||
|
c-4.839,0-9.148,7.407-11.907,18.909c-1.096,4.586-1.947,9.819-2.495,15.498c-0.432,4.551-0.665,9.391-0.665,14.399
|
||||||
|
s0.233,9.849,0.665,14.396c4.554,0.432,9.387,0.664,14.402,0.664c5.009,0,9.842-0.232,14.396-0.664
|
||||||
|
c10.011-0.95,18.653-2.875,24.775-5.411c6.046-2.501,9.624-5.615,9.624-8.985C184.963,142.832,181.858,133.388,176.541,125.569z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#231F20" d="M344.891,100.053c12.585,0,22.816,6.138,29.262,13.062l-10.064,11.326
|
||||||
|
c-5.353-5.192-11.175-8.495-19.041-8.495c-16.839,0-28.953,14.16-28.953,37.291c0,23.448,11.169,37.608,28.32,37.608
|
||||||
|
c9.128,0,15.895-3.775,21.717-10.228l10.067,11.169c-8.335,9.598-19.038,14.95-32.099,14.95c-26.119,0-46.731-18.88-46.731-53.025
|
||||||
|
C297.37,120.036,318.454,100.053,344.891,100.053z"/>
|
||||||
|
<path fill="#231F20" d="M416.961,125.701c19.352,0,36.822,14.793,36.822,40.597c0,25.647-17.471,40.439-36.822,40.439
|
||||||
|
c-19.197,0-36.66-14.792-36.66-40.439C380.301,140.494,397.764,125.701,416.961,125.701z M416.961,191.945
|
||||||
|
c11.33,0,18.25-10.228,18.25-25.647c0-15.577-6.92-25.804-18.25-25.804s-18.094,10.227-18.094,25.804
|
||||||
|
C398.867,181.717,405.631,191.945,416.961,191.945z"/>
|
||||||
|
<path fill="#231F20" d="M459.771,127.589h14.943l1.26,13.688h0.629c5.506-10.07,13.691-15.577,21.871-15.577
|
||||||
|
c3.938,0,6.455,0.472,8.811,1.574l-3.148,15.734c-2.67-0.784-4.717-1.257-8.018-1.257c-6.139,0-13.539,4.245-18.256,15.893v47.203
|
||||||
|
h-18.092V127.589z"/>
|
||||||
|
<path fill="#231F20" d="M541.121,125.701c20.928,0,31.941,15.107,31.941,36.667c0,3.458-0.314,6.604-0.787,8.495h-49.09
|
||||||
|
c1.57,14.003,10.379,21.869,22.811,21.869c6.613,0,12.273-2.041,17.941-5.662l6.135,11.326
|
||||||
|
c-7.395,4.878-16.676,8.341-26.432,8.341c-21.404,0-38.08-14.95-38.08-40.439C505.561,141.12,523.023,125.701,541.121,125.701z
|
||||||
|
M557.326,159.376c0-12.277-5.189-19.671-15.732-19.671c-9.125,0-16.996,6.768-18.57,19.671H557.326z"/>
|
||||||
|
<path fill="#F1606D" d="M600.602,152.607c0-32.729,17.785-53.344,42.799-53.344c24.863,0,42.641,20.615,42.641,53.344
|
||||||
|
c0,32.889-17.777,54.13-42.641,54.13C618.387,206.737,600.602,185.496,600.602,152.607z M678.49,152.607
|
||||||
|
c0-28.639-14.158-46.731-35.09-46.731c-21.084,0-35.248,18.093-35.248,46.731c0,28.796,14.164,47.521,35.248,47.521
|
||||||
|
C664.332,200.128,678.49,181.403,678.49,152.607z"/>
|
||||||
|
<path fill="#53A4D9" d="M699.738,186.125c7.557,8.495,18.412,14.003,30.529,14.003c15.732,0,25.807-8.499,25.807-20.767
|
||||||
|
c0-12.904-8.494-17.154-18.723-21.717l-15.736-7.082c-8.969-3.936-20.934-10.385-20.934-25.808
|
||||||
|
c0-14.947,12.904-25.492,30.059-25.492c12.588,0,22.658,5.665,28.949,12.435l-4.244,4.878c-5.982-6.452-14.32-10.7-24.705-10.7
|
||||||
|
c-13.691,0-22.816,7.239-22.816,18.565c0,11.962,10.385,16.521,17.936,19.985l15.738,6.921
|
||||||
|
c11.486,5.195,21.713,11.647,21.713,27.539s-13.061,27.851-33.201,27.851c-15.107,0-26.75-6.451-34.932-15.576L699.738,186.125z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
86
mod/dashboard/app/scripts/common/services/etcd.js
Normal file
86
mod/dashboard/app/scripts/common/services/etcd.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('etcd', [])
|
||||||
|
|
||||||
|
.factory('EtcdV2', ['$http', function($http) {
|
||||||
|
var keyPrefix = '/v2/keys/'
|
||||||
|
var statsPrefix = '/v2/stats/'
|
||||||
|
var baseURL = '/v2/'
|
||||||
|
|
||||||
|
delete $http.defaults.headers.common['X-Requested-With'];
|
||||||
|
|
||||||
|
function cleanupPath(path) {
|
||||||
|
var parts = path.split('/');
|
||||||
|
if (parts.length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
parts = parts.filter(function(v){return v!=='';});
|
||||||
|
parts = parts.join('/');
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
function newKey(keyName) {
|
||||||
|
var self = {};
|
||||||
|
self.name = cleanupPath(keyName);
|
||||||
|
|
||||||
|
self.getParent = function() {
|
||||||
|
var parts = self.name.split('/');
|
||||||
|
if (parts.length === 0) {
|
||||||
|
return newKey('');
|
||||||
|
}
|
||||||
|
parts.pop();
|
||||||
|
return newKey(parts.join('/'));
|
||||||
|
};
|
||||||
|
|
||||||
|
self.path = function() {
|
||||||
|
var path = '/' + cleanupPath(keyPrefix + self.name);
|
||||||
|
if (path === keyPrefix.substring(0, keyPrefix.length - 1)) {
|
||||||
|
return keyPrefix
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
};
|
||||||
|
|
||||||
|
self.get = function() {
|
||||||
|
return $http.get(self.path());
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set = function(keyValue) {
|
||||||
|
return $http({
|
||||||
|
url: self.path(),
|
||||||
|
data: $.param({value: keyValue}),
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.deleteKey = function(keyValue) {
|
||||||
|
return $http({
|
||||||
|
url: self.path(),
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newStat(statName) {
|
||||||
|
var self = {};
|
||||||
|
self.name = cleanupPath(statName);
|
||||||
|
|
||||||
|
self.path = function() {
|
||||||
|
return '/' + cleanupPath(statsPrefix + self.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.get = function() {
|
||||||
|
return $http.get(self.path());
|
||||||
|
};
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getStat: newStat,
|
||||||
|
getKey: newKey
|
||||||
|
}
|
||||||
|
}]);
|
191
mod/dashboard/app/scripts/controllers/browser.js
Normal file
191
mod/dashboard/app/scripts/controllers/browser.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('etcdBrowser', ['ngRoute', 'etcd', 'timeRelative'])
|
||||||
|
|
||||||
|
.constant('keyPrefix', '/v2/keys/')
|
||||||
|
|
||||||
|
.config(['$routeProvider', 'keyPrefix', function ($routeProvider, keyPrefix) {
|
||||||
|
//read localstorage
|
||||||
|
var previousPath = localStorage.getItem('etcd_path');
|
||||||
|
|
||||||
|
$routeProvider
|
||||||
|
.when('/', {
|
||||||
|
redirectTo: keyPrefix
|
||||||
|
})
|
||||||
|
.otherwise({
|
||||||
|
templateUrl: 'views/browser.html',
|
||||||
|
controller: 'MainCtrl'
|
||||||
|
});
|
||||||
|
}])
|
||||||
|
|
||||||
|
.controller('MainCtrl', ['$scope', '$location', 'EtcdV2', 'keyPrefix', function ($scope, $location, EtcdV2, keyPrefix) {
|
||||||
|
$scope.save = 'etcd-save-hide';
|
||||||
|
$scope.preview = 'etcd-preview-hide';
|
||||||
|
$scope.enableBack = true;
|
||||||
|
$scope.writingNew = false;
|
||||||
|
|
||||||
|
// etcdPath is the path to the key that is currenly being looked at.
|
||||||
|
$scope.etcdPath = $location.path();
|
||||||
|
|
||||||
|
$scope.$watch('etcdPath', function() {
|
||||||
|
function etcdPathKey() {
|
||||||
|
return pathKey($scope.etcdPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pathKey(path) {
|
||||||
|
var parts = path.split(keyPrefix);
|
||||||
|
if (parts.length === 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify everyone of the update
|
||||||
|
localStorage.setItem('etcdPath', $scope.etcdPath);
|
||||||
|
$scope.enableBack = true;
|
||||||
|
//disable back button if at root (/v2/keys/)
|
||||||
|
if($scope.etcdPath === keyPrefix) {
|
||||||
|
$scope.enableBack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.key = EtcdV2.getKey(etcdPathKey($scope.etcdPath));
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$watch('key', function() {
|
||||||
|
if ($scope.writingNew === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.key.get().success(function (data, status, headers, config) {
|
||||||
|
//hide any errors
|
||||||
|
$('#etcd-browse-error').hide();
|
||||||
|
// Looking at a directory if we got an array
|
||||||
|
if (data.dir === true) {
|
||||||
|
$scope.list = data.kvs;
|
||||||
|
$scope.preview = 'etcd-preview-hide';
|
||||||
|
} else {
|
||||||
|
$scope.singleValue = data.value;
|
||||||
|
$scope.preview = 'etcd-preview-reveal';
|
||||||
|
$scope.key.getParent().get().success(function(data) {
|
||||||
|
$scope.list = data.kvs;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$scope.previewMessage = 'No key selected.';
|
||||||
|
}).error(function (data, status, headers, config) {
|
||||||
|
$scope.previewMessage = 'Key does not exist.';
|
||||||
|
$scope.showBrowseError(data.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//back button click
|
||||||
|
$scope.back = function() {
|
||||||
|
$scope.etcdPath = $scope.key.getParent().path();
|
||||||
|
$scope.syncLocation();
|
||||||
|
$scope.preview = 'etcd-preview-hide';
|
||||||
|
$scope.writingNew = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.syncLocation = function() {
|
||||||
|
$location.path($scope.etcdPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showSave = function() {
|
||||||
|
$scope.save = 'etcd-save-reveal';
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.saveData = function() {
|
||||||
|
// TODO: fixup etcd to allow for empty values
|
||||||
|
$scope.key.set($scope.singleValue || ' ').success(function (data, status, headers, config) {
|
||||||
|
$scope.save = 'etcd-save-hide';
|
||||||
|
$scope.preview = 'etcd-preview-hide';
|
||||||
|
$scope.back();
|
||||||
|
$scope.writingNew = false;
|
||||||
|
}).error(function (data, status, headers, config) {
|
||||||
|
$scope.showSaveError(data.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteKey = function() {
|
||||||
|
$scope.key.deleteKey().success(function (data, status, headers, config) {
|
||||||
|
//TODO: remove loader
|
||||||
|
$scope.save = 'etcd-save-hide';
|
||||||
|
$scope.preview = 'etcd-preview-hide';
|
||||||
|
$scope.back();
|
||||||
|
}).error(function (data, status, headers, config) {
|
||||||
|
//TODO: remove loader
|
||||||
|
//show errors
|
||||||
|
$scope.showBrowseError('Could not delete the key');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.add = function() {
|
||||||
|
$scope.save = 'etcd-save-reveal';
|
||||||
|
$scope.preview = 'etcd-preview-reveal';
|
||||||
|
$scope.singleValue = '';
|
||||||
|
$('.etcd-browser-path').find('input').focus();
|
||||||
|
$scope.writingNew = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showBrowseError = function(message) {
|
||||||
|
$('#etcd-browse-error').find('.etcd-popover-content').text('Error: ' + message);
|
||||||
|
$('#etcd-browse-error').addClass('etcd-popover-right').show();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showSaveError = function(message) {
|
||||||
|
$('#etcd-save-error').find('.etcd-popover-content').text('Error: ' + message);
|
||||||
|
$('#etcd-save-error').addClass('etcd-popover-left').show();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getHeight = function() {
|
||||||
|
return $(window).height();
|
||||||
|
};
|
||||||
|
$scope.$watch($scope.getHeight, function() {
|
||||||
|
$('.etcd-body').css('height', $scope.getHeight()-45);
|
||||||
|
});
|
||||||
|
window.onresize = function(){
|
||||||
|
$scope.$apply();
|
||||||
|
};
|
||||||
|
|
||||||
|
}])
|
||||||
|
|
||||||
|
.directive('ngEnter', function() {
|
||||||
|
return function(scope, element, attrs) {
|
||||||
|
element.bind('keydown keypress', function(event) {
|
||||||
|
if(event.which === 13) {
|
||||||
|
scope.$apply(function(){
|
||||||
|
scope.$eval(attrs.ngEnter);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.directive('highlight', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: function(scope, element, attrs) {
|
||||||
|
if('#' + scope.etcdPath === attrs.href) {
|
||||||
|
element.parent().parent().addClass('etcd-selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
moment.lang('en', {
|
||||||
|
relativeTime : {
|
||||||
|
future: 'Expires in %s',
|
||||||
|
past: 'Expired %s ago',
|
||||||
|
s: 'seconds',
|
||||||
|
m: 'a minute',
|
||||||
|
mm: '%d minutes',
|
||||||
|
h: 'an hour',
|
||||||
|
hh: '%d hours',
|
||||||
|
d: 'a day',
|
||||||
|
dd: '%d days',
|
||||||
|
M: 'a month',
|
||||||
|
MM: '%d months',
|
||||||
|
y: 'a year',
|
||||||
|
yy: '%d years'
|
||||||
|
}
|
||||||
|
});
|
199
mod/dashboard/app/scripts/controllers/stats.js
Normal file
199
mod/dashboard/app/scripts/controllers/stats.js
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('etcdStats', ['ngRoute', 'etcd'])
|
||||||
|
|
||||||
|
.config(['$routeProvider', function ($routeProvider) {
|
||||||
|
$routeProvider
|
||||||
|
.when('/', {
|
||||||
|
templateUrl: 'views/stats.html',
|
||||||
|
controller: 'StatsCtrl'
|
||||||
|
})
|
||||||
|
.otherwise({
|
||||||
|
templateUrl: 'views/stats.html',
|
||||||
|
controller: 'StatsCtrl'
|
||||||
|
});
|
||||||
|
}])
|
||||||
|
|
||||||
|
.controller('StatsCtrl', ['$scope', 'EtcdV2', 'statsVega', function ($scope, EtcdV2, statsVega) {
|
||||||
|
$scope.graphContainer = '#latency';
|
||||||
|
$scope.graphVisibility = 'etcd-graph-show';
|
||||||
|
$scope.tableVisibility = 'etcd-table-hide';
|
||||||
|
|
||||||
|
//make requests
|
||||||
|
function readStats() {
|
||||||
|
EtcdV2.getStat('leader').get().success(function(data) {
|
||||||
|
$scope.leaderStats = data;
|
||||||
|
$scope.leaderName = data.leader;
|
||||||
|
$scope.machines = [];
|
||||||
|
//hardcode leader stats
|
||||||
|
$scope.machines.push({
|
||||||
|
latency: {
|
||||||
|
average: 0,
|
||||||
|
current: 0,
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 0,
|
||||||
|
standardDeviation: 0
|
||||||
|
},
|
||||||
|
name: data.leader
|
||||||
|
});
|
||||||
|
$.each(data.followers, function(index, value) {
|
||||||
|
value.name = index;
|
||||||
|
$scope.machines.push(value);
|
||||||
|
});
|
||||||
|
//sort array so machines don't jump when output
|
||||||
|
$scope.machines.sort(function(a, b){
|
||||||
|
if(a.name < b.name) return -1;
|
||||||
|
if(a.name > b.name) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
drawGraph();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGraph () {
|
||||||
|
//hardcoded padding from chart json
|
||||||
|
var vertPadding = 30;
|
||||||
|
var horzPadding = 15;
|
||||||
|
//fetch width and height of graph area
|
||||||
|
var width = $($scope.graphContainer).width() - horzPadding;
|
||||||
|
var height = $($scope.graphContainer).height() - vertPadding;
|
||||||
|
|
||||||
|
// parse a spec and create a visualization view
|
||||||
|
function parse(spec) {
|
||||||
|
vg.parse.spec(spec, function(chart) {
|
||||||
|
chart({
|
||||||
|
el: $scope.graphContainer,
|
||||||
|
data: {
|
||||||
|
'stats': $scope.machines
|
||||||
|
}
|
||||||
|
}).width(width).height(height).update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
parse(statsVega);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.showTable = function() {
|
||||||
|
$scope.tableVisibility = 'etcd-table-reveal';
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showGraph = function() {
|
||||||
|
$scope.tableVisibility = 'etcd-table-hide';
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.getHeight = function() {
|
||||||
|
return $(window).height();
|
||||||
|
};
|
||||||
|
$scope.getWidth = function() {
|
||||||
|
return $(window).width();
|
||||||
|
};
|
||||||
|
$scope.$watch($scope.getHeight, function() {
|
||||||
|
$('.etcd-body').css('height', $scope.getHeight()-5);
|
||||||
|
readStats();
|
||||||
|
});
|
||||||
|
$scope.$watch($scope.getWidth, function() {
|
||||||
|
readStats();
|
||||||
|
});
|
||||||
|
window.onresize = function(){
|
||||||
|
$scope.$apply();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the graphs live
|
||||||
|
setInterval(function() {
|
||||||
|
readStats();
|
||||||
|
$scope.$apply();
|
||||||
|
}, 500);
|
||||||
|
}])
|
||||||
|
|
||||||
|
|
||||||
|
/* statsVega returns the vega configuration for the stats dashboard */
|
||||||
|
.factory('statsVega', function () {
|
||||||
|
return {
|
||||||
|
'padding': {'top': 10, 'left': 5, 'bottom': 40, 'right': 10},
|
||||||
|
'data': [
|
||||||
|
{
|
||||||
|
'name': 'stats'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'thresholds',
|
||||||
|
'values': [50, 100]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'scales': [
|
||||||
|
{
|
||||||
|
'name': 'y',
|
||||||
|
'type': 'ordinal',
|
||||||
|
'range': 'height',
|
||||||
|
'domain': {'data': 'stats', 'field': 'index'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'x',
|
||||||
|
'range': 'width',
|
||||||
|
'domainMin': 0,
|
||||||
|
'domainMax': 100,
|
||||||
|
'nice': true,
|
||||||
|
'zero': true,
|
||||||
|
'domain': {'data': 'stats', 'field': 'data.latency.current'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'color',
|
||||||
|
'type': 'linear',
|
||||||
|
'domain': [10, 50, 100, 1000000000],
|
||||||
|
'range': ['#00DB24', '#FFC000', '#c40022', '#c40022']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'axes': [
|
||||||
|
{
|
||||||
|
'type': 'x',
|
||||||
|
'scale': 'x',
|
||||||
|
'ticks': 6,
|
||||||
|
'name': 'Latency (ms)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'y',
|
||||||
|
'scale': 'y',
|
||||||
|
'properties': {
|
||||||
|
'ticks': {
|
||||||
|
'stroke': {'value': 'transparent'}
|
||||||
|
},
|
||||||
|
'majorTicks': {
|
||||||
|
'stroke': {'value': 'transparent'}
|
||||||
|
},
|
||||||
|
'labels': {
|
||||||
|
'fill': {'value': 'transparent'}
|
||||||
|
},
|
||||||
|
'axis': {
|
||||||
|
'stroke': {'value': '#333'},
|
||||||
|
'strokeWidth': {'value': 1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'marks': [
|
||||||
|
{
|
||||||
|
'type': 'rect',
|
||||||
|
'from': {'data': 'stats'},
|
||||||
|
'properties': {
|
||||||
|
'enter': {
|
||||||
|
'x': {'scale': 'x', 'value': 0},
|
||||||
|
'x2': {'scale': 'x', 'field': 'data.latency.current'},
|
||||||
|
'y': {'scale': 'y', 'field': 'index', 'offset': -1},
|
||||||
|
'height': {'value': 3},
|
||||||
|
'fill': {'scale':'color', 'field':'data.latency.current'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'symbol',
|
||||||
|
'from': {'data': 'stats'},
|
||||||
|
'properties': {
|
||||||
|
'enter': {
|
||||||
|
'x': {'scale': 'x', 'field': 'data.latency.current'},
|
||||||
|
'y': {'scale': 'y', 'field': 'index'},
|
||||||
|
'size': {'value': 50},
|
||||||
|
'fill': {'value': '#000'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
1
mod/dashboard/app/scripts/ng-time-relative.min.js
vendored
Normal file
1
mod/dashboard/app/scripts/ng-time-relative.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(function(e){if("function"==typeof bootstrap)bootstrap("ng-time-relative",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeNgTimeRelative=e}else"undefined"!=typeof window?window.ngTimeRelative=e():global.ngTimeRelative=e()})(function(){var define,ses,bootstrap,module,exports;return function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0](function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i}({1:[function(require,module,exports){"use strict";exports=module.exports=function(module){module.constant("timeRelativeConfig",{calendar:{en:{lastDay:"[Yesterday], LT",sameDay:"[Today], LT",nextDay:"[Tomorrow], LT",lastWeek:"dddd, LT",nextWeek:"Next dddd, LT",sameElse:"LL"}}}).directive("relative",["$timeout","moment",directive]).run(function(moment,timeRelativeConfig){angular.forEach(timeRelativeConfig.calendar,function(translation,lang){moment.lang(lang,{calendar:translation})})})};exports.directive=directive;if(angular){var mod=angular.module("timeRelative",[]);if(moment){mod.constant("moment",moment);moment.lang("en",{})}exports(mod)}function directive($timeout,moment){return{restrict:"AC",scope:{datetime:"@"},link:function(scope,element,attrs){var timeout;scope.$watch("datetime",function(dateString){$timeout.cancel(timeout);var date=moment(dateString);if(!date)return;var to=function(){return moment(attrs.to)};var withoutSuffix="withoutSuffix"in attrs;if(!attrs.title)element.attr("title",date.format("LLLL"));function updateTime(){element.text(diffString(date,to()))}function diffString(a,b){if(Math.abs(a.clone().startOf("day").diff(b,"days",true))<1)return a.from(b,withoutSuffix);else return a.calendar(b)}function updateLater(){updateTime();timeout=$timeout(function(){updateLater()},nextUpdateIn())}function nextUpdateIn(){var delta=Math.abs(moment().diff(date));if(delta<45e3)return 45e3-delta;if(delta<9e4)return 9e4-delta;if(delta<45*6e4)return 6e4-(delta+3e4)%6e4;return 366e4-delta%36e5}element.bind("$destroy",function(){$timeout.cancel(timeout)});updateLater()})}}}},{}]},{},[1])});
|
6970
mod/dashboard/app/scripts/vega.js
Normal file
6970
mod/dashboard/app/scripts/vega.js
Normal file
File diff suppressed because it is too large
Load Diff
50
mod/dashboard/app/stats.html
Normal file
50
mod/dashboard/app/stats.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
|
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>etcd Browser</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
|
||||||
|
|
||||||
|
<!-- build:css(.tmp) styles/main.css -->
|
||||||
|
<link rel="stylesheet" href="styles/etcd-widgets.css">
|
||||||
|
<link href="http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,400italic,600,700,900" rel="stylesheet" type="text/css">
|
||||||
|
<link href="http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600,700" rel="stylesheet" type="text/css">
|
||||||
|
<!-- endbuild -->
|
||||||
|
</head>
|
||||||
|
<body ng-app="etcdStats">
|
||||||
|
<!--[if lt IE 7]>
|
||||||
|
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="bower_components/es5-shim/es5-shim.js"></script>
|
||||||
|
<script src="bower_components/json3/lib/json3.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<!-- Add your site or application content here -->
|
||||||
|
<div id="etd_stats" ng-view="etcd">
|
||||||
|
</div>
|
||||||
|
<!-- build:js scripts/stats-modules.js -->
|
||||||
|
<script src="bower_components/jquery/jquery.js"></script>
|
||||||
|
<script src="bower_components/angular/angular.js"></script>
|
||||||
|
<script src="bower_components/angular-resource/angular-resource.js"></script>
|
||||||
|
<script src="bower_components/angular-route/angular-route.js"></script>
|
||||||
|
<script src="bower_components/angular-cookies/angular-cookies.js"></script>
|
||||||
|
<script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
|
||||||
|
<script src="bower_components/d3/d3.js"></script>
|
||||||
|
<script src="bower_components/underscore/underscore.js"></script>
|
||||||
|
<!-- endbuild -->
|
||||||
|
|
||||||
|
<!-- build:js({.tmp,app}) scripts/stats-scripts.js -->
|
||||||
|
<script src="scripts/vega.js"></script>
|
||||||
|
<script src="scripts/common/services/etcd.js"></script>
|
||||||
|
<script src="scripts/controllers/stats.js"></script>
|
||||||
|
<!-- endbuild -->
|
||||||
|
</body>
|
||||||
|
</html>
|
6167
mod/dashboard/app/styles/bootstrap.css
vendored
Normal file
6167
mod/dashboard/app/styles/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
713
mod/dashboard/app/styles/etcd-widgets.css
Normal file
713
mod/dashboard/app/styles/etcd-widgets.css
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
.etcd-container {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 3px;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #2176AC;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0,0,0,.5);
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:focus {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 22px;
|
||||||
|
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-button {
|
||||||
|
display:inline-block;
|
||||||
|
padding:6px 12px;
|
||||||
|
margin-bottom:0;
|
||||||
|
font-size:14px;
|
||||||
|
font-weight:normal;
|
||||||
|
line-height:1.428571429;
|
||||||
|
text-align:center;
|
||||||
|
white-space:nowrap;
|
||||||
|
vertical-align:middle;
|
||||||
|
cursor:pointer;
|
||||||
|
border:1px solid transparent;
|
||||||
|
border-radius:4px;
|
||||||
|
-webkit-user-select:none;
|
||||||
|
-moz-user-select:none;
|
||||||
|
-ms-user-select:none;
|
||||||
|
-o-user-select:none;
|
||||||
|
user-select:none;
|
||||||
|
margin: 0px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-button.etcd-button-small {
|
||||||
|
height: 25px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-button-primary {
|
||||||
|
background-color: #428BCA;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 3px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-button-primary:active {
|
||||||
|
background-color: #2276ad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover {
|
||||||
|
background: #333;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 15px;
|
||||||
|
position: absolute;
|
||||||
|
top: 39px;
|
||||||
|
z-index: 9999;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
box-shadow: 0px 2px 10px rgba(0,0,0,.5);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover-error .etcd-popover-content {
|
||||||
|
color: #FF3C43;
|
||||||
|
font-weight: bold;
|
||||||
|
user-select: text;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
-moz-user-select: text;
|
||||||
|
-ms-user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover-notch {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-moz-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
position: absolute;
|
||||||
|
margin-top: -5px;
|
||||||
|
margin-left: 3px;
|
||||||
|
background: #333;
|
||||||
|
top: 0px;
|
||||||
|
right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover.etcd-popover-right {
|
||||||
|
left: 77px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover-right .etcd-popover-notch {
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover.etcd-popover-left {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover-left .etcd-popover-notch {
|
||||||
|
right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover-confirm {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-popover-confirm button {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-header {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.etcd-header.solid {
|
||||||
|
background: #eeeeee;
|
||||||
|
background: -moz-linear-gradient(top, #eeeeee 0%, #dddddd 100%);
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eeeeee), color-stop(100%,#dddddd));
|
||||||
|
background: -webkit-linear-gradient(top, #eeeeee 0%,#dddddd 100%);
|
||||||
|
background: -o-linear-gradient(top, #eeeeee 0%,#dddddd 100%);
|
||||||
|
background: -ms-linear-gradient(top, #eeeeee 0%,#dddddd 100%);
|
||||||
|
background: linear-gradient(to bottom, #eeeeee 0%,#dddddd 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#dddddd',GradientType=0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body {
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table thead td {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 20px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
padding-left: 0px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table tbody td {
|
||||||
|
line-height: 18px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
padding-left: 0px;
|
||||||
|
vertical-align: text-top;
|
||||||
|
user-select: text;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
-moz-user-select: text;
|
||||||
|
-ms-user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table .etcd-ttl-header {
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table tbody .etcd-ttl {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table tbody .etcd-ttl .etcd-ttl-none {
|
||||||
|
color: #999;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table .etcd-actions-header {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table thead td:first-child, .etcd-body table tbody td:first-child {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body table thead td:last-child, .etcd-body table tbody td:last-child {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container .etcd-preview .etcd-dialog {
|
||||||
|
background: #333;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
left: 0px;
|
||||||
|
padding: 20px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
bottom: 0px;
|
||||||
|
opacity: 0;
|
||||||
|
min-height: 110px; /* REMOVE ME! */
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 150ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container .etcd-preview .etcd-dialog .etcd-dialog-message {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container .etcd-preview .etcd-dialog .etcd-dialog-buttons a {
|
||||||
|
line-height: 34px;
|
||||||
|
color: #fff;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.etcd-container .etcd-preview .etcd-dialog.etcd-reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container .etcd-preview .etcd-dialog.etcd-hide {
|
||||||
|
opacity: 0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.etcd-body .etcd-list {
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list .etcd-selected {
|
||||||
|
background-color: #EAF3FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list a.directory {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list tr:hover .etcd-delete svg {
|
||||||
|
1visibility: visible;
|
||||||
|
fill: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list .etcd-delete {
|
||||||
|
height: 20px;
|
||||||
|
width: 25px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list .etcd-delete svg {
|
||||||
|
height: 20px;
|
||||||
|
fill: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list .etcd-selected .etcd-delete svg {
|
||||||
|
height: 20px;
|
||||||
|
fill: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-body .etcd-list .etcd-delete:hover svg {
|
||||||
|
cursor: pointer;
|
||||||
|
fill: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-header {
|
||||||
|
height: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-back {
|
||||||
|
height: 37px;
|
||||||
|
width: 37px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 0px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 3px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-back {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-back {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-back svg {
|
||||||
|
height: 20px;
|
||||||
|
padding: 8px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-back:hover svg {
|
||||||
|
cursor: pointer;
|
||||||
|
fill: #428bca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-back.etcd-disabled svg {
|
||||||
|
fill: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-add {
|
||||||
|
height: 37px;
|
||||||
|
width: 37px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 0px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-add {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-add {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-add svg {
|
||||||
|
height: 22px;
|
||||||
|
padding: 7px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-add:hover svg {
|
||||||
|
cursor: pointer;
|
||||||
|
fill: #428bca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-add.etcd-disabled svg {
|
||||||
|
fill: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-header .etcd-browser-path {
|
||||||
|
position: absolute;
|
||||||
|
left: 72px;
|
||||||
|
right: 0px;
|
||||||
|
top: 0;
|
||||||
|
margin: 6px 5px 6px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-header .etcd-browser-path input {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-header .etcd-save {
|
||||||
|
position: absolute;
|
||||||
|
width: 54px;
|
||||||
|
right: -55px;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-save-reveal .etcd-header .etcd-save {
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-save-reveal .etcd-header .etcd-browser-path {
|
||||||
|
right: 62px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-save-hide .etcd-header .etcd-save {
|
||||||
|
right: -55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-save-hide .etcd-header .etcd-browser-path {
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-preview {
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
top: 0px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-preview pre, .etcd-container.etcd-browser .etcd-preview textarea {
|
||||||
|
padding: 20px 20px 20px 20px;
|
||||||
|
margin: 0px;
|
||||||
|
font-family: Consolas, "Liberation Mono", Courier, monospace;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 13px;
|
||||||
|
border: 1px;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview pre, .etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview textarea {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-preview .etcd-empty {
|
||||||
|
top: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-preview .etcd-empty-message {
|
||||||
|
margin-top: 25%;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single Column Positioning */
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.etcd-container.etcd-browser .etcd-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-list {
|
||||||
|
left: -100%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-list {
|
||||||
|
left: 0%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-preview {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview {
|
||||||
|
left: -1px;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-preview {
|
||||||
|
left: 100%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Double Column Positioning */
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
.etcd-container.etcd-browser .etcd-list {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser .etcd-preview {
|
||||||
|
left: 50%;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview {
|
||||||
|
left: 50%; /* does nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview .etcd-empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-preview {
|
||||||
|
left: 50%; /* does nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-preview .etcd-empty {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-browser.etcd-preview-hide .etcd-preview pre, .etcd-container.etcd-browser.etcd-preview-hide .etcd-preview textarea {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats h2 {
|
||||||
|
margin-top: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-format-selector {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-format-selector .etcd-selector-item {
|
||||||
|
display: inline-block;
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
padding: 8px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-format-selector .etcd-selector-item:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-format-selector .etcd-selector-item svg {
|
||||||
|
fill: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-graph {
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-graph .etcd-graph-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 60px;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
right: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats table .etcd-latency {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list {
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
top: 0px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list .etcd-square {
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list .etcd-square-red {
|
||||||
|
background-color: #c40022;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list .etcd-square-orange {
|
||||||
|
background-color: #FFC000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list .etcd-square-green {
|
||||||
|
background-color: #00DB24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list .etcd-machine-type {
|
||||||
|
color: #999;
|
||||||
|
padding-left: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-list .etcd-latency-value {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single Column Positioning */
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.etcd-container.etcd-stats .etcd-list {
|
||||||
|
width: 100%;
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-graph {
|
||||||
|
left: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats.etcd-table-reveal .etcd-graph {
|
||||||
|
left: -100%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
.etcd-container.etcd-stats.etcd-table-hide .etcd-graph {
|
||||||
|
left: 0%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
|
||||||
|
}
|
||||||
|
.etcd-container.etcd-stats.etcd-table-hide .etcd-format-selector .etcd-selector-graph svg * {
|
||||||
|
fill: #428bca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats.etcd-table-hide .etcd-list {
|
||||||
|
left: 100%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
.etcd-container.etcd-stats.etcd-table-reveal .etcd-list {
|
||||||
|
left: 0%;
|
||||||
|
transition-property: all;
|
||||||
|
transition-duration: 250ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
.etcd-container.etcd-stats.etcd-table-reveal .etcd-format-selector .etcd-selector-table svg * {
|
||||||
|
fill: #428bca;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Double Column Positioning */
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
.etcd-container.etcd-stats .etcd-list {
|
||||||
|
width: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-graph {
|
||||||
|
left: 0%;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etcd-container.etcd-stats .etcd-format-selector {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
mod/dashboard/app/styles/main.css
Normal file
22
mod/dashboard/app/styles/main.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
body {
|
||||||
|
background: #fafafa;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-unit {
|
||||||
|
margin: 50px auto 0 auto;
|
||||||
|
width: 300px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 200;
|
||||||
|
line-height: 30px;
|
||||||
|
background-color: #eee;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-unit h1 {
|
||||||
|
font-size: 60px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
99
mod/dashboard/app/views/browser.html
Normal file
99
mod/dashboard/app/views/browser.html
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<div class="etcd-container etcd-browser {{columns}} {{preview}} {{save}}">
|
||||||
|
<!--
|
||||||
|
<div class="etcd-popover etcd-popover-error">
|
||||||
|
<div class="etcd-popover-notch"></div>
|
||||||
|
<div class="etcd-popover-content">
|
||||||
|
Overwrite this value?
|
||||||
|
</div>
|
||||||
|
<div class="etcd-popover-confirm">
|
||||||
|
<button class="etcd-button etcd-button-small etcd-button-primary etcd-confirm">Overwrite</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<div class="etcd-popover etcd-popover-error" id="etcd-save-error">
|
||||||
|
<div class="etcd-popover-notch"></div>
|
||||||
|
<div class="etcd-popover-content">
|
||||||
|
Error:
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-popover etcd-popover-error" id="etcd-browse-error">
|
||||||
|
<div class="etcd-popover-notch"></div>
|
||||||
|
<div class="etcd-popover-content">
|
||||||
|
Error:
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-header solid">
|
||||||
|
<a class="etcd-back" ng-click="back()" ng-class="{false:'etcd-disabled'}[enableBack]">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
preserveAspectRatio="xMinYMin" viewBox="0 0 73.356 61" enable-background="new 0 0 73.356 61" xml:space="preserve">
|
||||||
|
<path d="M5.27,33.226l22.428,22.428c1.562,1.562,4.095,1.562,5.657,0c1.562-1.562,1.562-4.095,0-5.657L17.77,34.413h48.514
|
||||||
|
c2.209,0,4-1.791,4-4s-1.791-4-4-4H17.749l15.604-15.582c1.563-1.561,1.565-4.094,0.004-5.657C32.576,4.391,31.552,4,30.527,4
|
||||||
|
c-1.023,0-2.046,0.39-2.827,1.169L5.272,27.567c-0.751,0.75-1.173,1.768-1.173,2.829C4.098,31.458,4.52,32.476,5.27,33.226z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a class="etcd-add">
|
||||||
|
|
||||||
|
<svg version="1.1" ng-click="add()" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
preserveAspectRatio="xMinYMin" viewBox="0 0 72.556 61" enable-background="new 0 0 72.556 61" xml:space="preserve">
|
||||||
|
<path d="M34.521,8v11.088v23v10.737c0,2.209,1.791,4,4,4c2.209,0,4-1.791,4-4V42.067V19.109V8c0-2.209-1.791-4-4-4
|
||||||
|
C36.312,4,34.521,5.791,34.521,8z"/>
|
||||||
|
<path d="M16.109,34.412h11.088h23h10.737c2.209,0,4-1.791,4-4c0-2.209-1.791-4-4-4H50.175H27.217H16.109c-2.209,0-4,1.791-4,4
|
||||||
|
C12.109,32.621,13.9,34.412,16.109,34.412z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<div class="etcd-browser-path">
|
||||||
|
<input type="text" ng-model="etcdPath" ng-enter="syncLocation()" tabindex="888" />
|
||||||
|
</div>
|
||||||
|
<button class="etcd-button etcd-button-small etcd-button-primary etcd-save" ng-click="saveData()">Save</button>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-body">
|
||||||
|
<div class="etcd-list">
|
||||||
|
<table cellpadding="0" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<td class="etcd-name-header">Name</td>
|
||||||
|
<td class="etcd-ttl-header">TTL</td>
|
||||||
|
<td class="etcd-actions-header"> </td>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="key in list | orderBy:'key'">
|
||||||
|
<td><a ng-class="{true:'directory'}[key.dir]" href="#/v2/keys{{key.key}}" highlight>{{key.key}}</a></td>
|
||||||
|
<td ng-switch on="!!key.expiration" class="etcd-ttl">
|
||||||
|
<div ng-switch-when="true"><time relative datetime="{{key.expiration.substring(0, key.expiration.lastIndexOf('-'))}}"></time></div>
|
||||||
|
<div ng-switch-default class="etcd-ttl-none">—</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="etcd-actions">
|
||||||
|
<div class="etcd-delete" ng-switch on="!!key.dir">
|
||||||
|
<svg ng-switch-when="false" ng-click="deleteKey()" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
x="0px" y="0px" preserveAspectRatio="xMinYMin" viewBox="0 0 76.143 61" enable-background="new 0 0 76.143 61" xml:space="preserve">
|
||||||
|
<path d="M49.41,13.505l-6.035,6.035L27.112,35.803l-6.035,6.035c-1.562,1.562-1.562,4.095,0,5.657c1.562,1.562,4.095,1.562,5.657,0
|
||||||
|
l6.05-6.05l16.234-16.234l6.05-6.05c1.562-1.562,1.562-4.095,0-5.657C53.505,11.943,50.972,11.943,49.41,13.505z"/>
|
||||||
|
<path d="M21.077,19.162l6.035,6.035L43.375,41.46l6.035,6.035c1.562,1.562,4.095,1.562,5.657,0c1.562-1.562,1.562-4.095,0-5.657
|
||||||
|
l-6.05-6.05L32.783,19.555l-6.05-6.05c-1.562-1.562-4.095-1.562-5.657,0C19.515,15.067,19.515,17.6,21.077,19.162z"/>
|
||||||
|
</svg>
|
||||||
|
<div ng-switch-when="true"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-preview">
|
||||||
|
<textarea placeholder="Enter a key name above and the value here" ng-model="singleValue" tabindex="888" ng-change="showSave()">
|
||||||
|
</textarea>
|
||||||
|
<div class="etcd-empty">
|
||||||
|
<div class="etcd-empty-message">{{preview_message}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-dialog">
|
||||||
|
<div class="etcd-dialog-message">
|
||||||
|
Save and replicate this change?
|
||||||
|
</div>
|
||||||
|
<div class="etcd-dialog-buttons">
|
||||||
|
<button class="etcd-button etcd-button-primary">Save Changes</button>
|
||||||
|
<a href="javascript:void(0);">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
49
mod/dashboard/app/views/stats.html
Normal file
49
mod/dashboard/app/views/stats.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<div class="etcd-container etcd-stats {{columns}} {{tableVisibility}}">
|
||||||
|
<div class="etcd-body">
|
||||||
|
<div class="etcd-format-selector">
|
||||||
|
<div class="etcd-selector-item etcd-selector-graph" ng-click="showGraph()">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
preserveAspectRatio="xMinYMin" viewBox="0 0 60 60.007" enable-background="new 0 0 60 60.007" xml:space="preserve">
|
||||||
|
<path fill="#1D1D1B" d="M30-0.091c-16.621,0-30.094,13.474-30.094,30.094c0,16.621,13.474,30.094,30.094,30.094
|
||||||
|
s30.094-13.474,30.094-30.094C60.094,13.383,46.621-0.091,30-0.091z M30,47.239c-9.519,0-17.235-7.716-17.235-17.235
|
||||||
|
S20.481,12.768,30,12.768c9.519,0,17.235,7.716,17.235,17.235S39.519,47.239,30,47.239z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="etcd-selector-item etcd-selector-table" ng-click="showTable()">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
preserveAspectRatio="xMinYMin" viewBox="0 0 59.496 59.503" enable-background="new 0 0 59.496 59.503" xml:space="preserve">
|
||||||
|
<rect x="0" y="0" fill="#1D1D1B" width="59.496" height="13.111"/>
|
||||||
|
<rect x="0" y="23.196" fill="#1D1D1B" width="59.496" height="13.111"/>
|
||||||
|
<rect x="0" y="46.392" fill="#1D1D1B" width="59.496" height="13.111"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-graph">
|
||||||
|
<h2>Machine Latency</h2>
|
||||||
|
<div class="etcd-graph-container" id="latency">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="etcd-list">
|
||||||
|
<h2>Machine List</h2>
|
||||||
|
<table cellpadding="0" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<td class="etcd-name-header">Machine Name</td>
|
||||||
|
<td class="etcd-latency">Latency</td>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="machine in machines">
|
||||||
|
<td ng-switch on="{true:'leader', false: 'follower'}[leaderName == machine.name]">
|
||||||
|
<div ng-switch-when="leader">{{machine.name}}<span class="etcd-machine-type">(leader)</span></div>
|
||||||
|
<div ng-switch-default>{{machine.name}}</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="etcd-square ng-class: {'etcd-square-green': machine.latency.current < 25, 'etcd-square-orange': machine.latency.current < 60, 'etcd-square-red': machine.latency.current >= 60}"></div>
|
||||||
|
<div class="etcd-latency-value">{{machine.latency.current | number:1 }} ms</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
22
mod/dashboard/bower.json
Normal file
22
mod/dashboard/bower.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "etcdDashboard",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"angular": "~1.2.0-rc.2",
|
||||||
|
"json3": "~3.2.4",
|
||||||
|
"jquery": "~1.9.1",
|
||||||
|
"bootstrap-sass": "~2.3.1",
|
||||||
|
"es5-shim": "~2.0.8",
|
||||||
|
"angular-route": "~1.2.0-rc.2",
|
||||||
|
"angular-resource": "~1.2.0-rc.2",
|
||||||
|
"angular-cookies": "~1.2.0-rc.2",
|
||||||
|
"angular-sanitize": "~1.2.0-rc.2",
|
||||||
|
"d3": "~3.3.6",
|
||||||
|
"moment": "~2.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"angular-mocks": "~1.2.0-rc.2",
|
||||||
|
"angular-scenario": "~1.2.0-rc.2",
|
||||||
|
"underscore": "~1.5.2"
|
||||||
|
}
|
||||||
|
}
|
17
mod/dashboard/build
Executable file
17
mod/dashboard/build
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
cd ${DIR}
|
||||||
|
git clean -x -f dist
|
||||||
|
|
||||||
|
npm install
|
||||||
|
bower install
|
||||||
|
grunt build
|
||||||
|
|
||||||
|
export GOPATH="${DIR}/../../"
|
||||||
|
|
||||||
|
for i in `find dist -type f`; do
|
||||||
|
file=$(echo $i | sed 's#dist/##g' | sed 's#/#-#g')
|
||||||
|
go build github.com/jteeuwen/go-bindata
|
||||||
|
./go-bindata -nomemcopy -pkg "resources" -toc -out resources/$file.go -prefix dist $i
|
||||||
|
done
|
52
mod/dashboard/dashboard.go
Normal file
52
mod/dashboard/dashboard.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/log"
|
||||||
|
"github.com/coreos/etcd/mod/dashboard/resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
func memoryFileServer(w http.ResponseWriter, req *http.Request) {
|
||||||
|
log.Debugf("[recv] %s %s [%s]", req.Method, req.URL.Path, req.RemoteAddr)
|
||||||
|
upath := req.URL.Path
|
||||||
|
if len(upath) == 0 {
|
||||||
|
upath = "index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use the new mux to do this work
|
||||||
|
dir, file := path.Split(upath)
|
||||||
|
if file == "browser" || file == "stats" {
|
||||||
|
file = file + ".html"
|
||||||
|
}
|
||||||
|
upath = path.Join(dir, file)
|
||||||
|
b, ok := resources.File("/" + upath)
|
||||||
|
|
||||||
|
if ok == false {
|
||||||
|
http.Error(w, upath+": File not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeContent(w, req, upath, time.Time{}, bytes.NewReader(b))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DashboardHttpHandler either uses the compiled in virtual filesystem for the
|
||||||
|
// dashboard assets or if ETCD_DASHBOARD_DIR is set uses that as the source of
|
||||||
|
// assets.
|
||||||
|
func HttpHandler() (handler http.Handler) {
|
||||||
|
handler = http.HandlerFunc(memoryFileServer)
|
||||||
|
|
||||||
|
// Serve the dashboard from a filesystem if the magic env variable is enabled
|
||||||
|
dashDir := os.Getenv("ETCD_DASHBOARD_DIR")
|
||||||
|
if len(dashDir) != 0 {
|
||||||
|
log.Debugf("Using dashboard directory %s", dashDir)
|
||||||
|
handler = http.FileServer(http.Dir(dashDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler
|
||||||
|
}
|
54
mod/dashboard/karma-e2e.conf.js
Normal file
54
mod/dashboard/karma-e2e.conf.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Karma configuration
|
||||||
|
// http://karma-runner.github.io/0.10/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function(config) {
|
||||||
|
config.set({
|
||||||
|
// base path, that will be used to resolve files and exclude
|
||||||
|
basePath: '',
|
||||||
|
|
||||||
|
// testing framework to use (jasmine/mocha/qunit/...)
|
||||||
|
frameworks: ['ng-scenario'],
|
||||||
|
|
||||||
|
// list of files / patterns to load in the browser
|
||||||
|
files: [
|
||||||
|
'test/e2e/**/*.js'
|
||||||
|
],
|
||||||
|
|
||||||
|
// list of files / patterns to exclude
|
||||||
|
exclude: [],
|
||||||
|
|
||||||
|
// web server port
|
||||||
|
port: 8080,
|
||||||
|
|
||||||
|
// level of logging
|
||||||
|
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
|
||||||
|
|
||||||
|
// enable / disable watching file and executing tests whenever any file changes
|
||||||
|
autoWatch: false,
|
||||||
|
|
||||||
|
|
||||||
|
// Start these browsers, currently available:
|
||||||
|
// - Chrome
|
||||||
|
// - ChromeCanary
|
||||||
|
// - Firefox
|
||||||
|
// - Opera
|
||||||
|
// - Safari (only Mac)
|
||||||
|
// - PhantomJS
|
||||||
|
// - IE (only Windows)
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
|
|
||||||
|
// Continuous Integration mode
|
||||||
|
// if true, it capture browsers, run tests and exit
|
||||||
|
singleRun: false
|
||||||
|
|
||||||
|
// Uncomment the following lines if you are using grunt's server to run the tests
|
||||||
|
// proxies: {
|
||||||
|
// '/': 'http://localhost:9000/'
|
||||||
|
// },
|
||||||
|
// URL root prevent conflicts with the site root
|
||||||
|
// urlRoot: '_karma_'
|
||||||
|
});
|
||||||
|
};
|
52
mod/dashboard/karma.conf.js
Normal file
52
mod/dashboard/karma.conf.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Karma configuration
|
||||||
|
// http://karma-runner.github.io/0.10/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function(config) {
|
||||||
|
config.set({
|
||||||
|
// base path, that will be used to resolve files and exclude
|
||||||
|
basePath: '',
|
||||||
|
|
||||||
|
// testing framework to use (jasmine/mocha/qunit/...)
|
||||||
|
frameworks: ['jasmine'],
|
||||||
|
|
||||||
|
// list of files / patterns to load in the browser
|
||||||
|
files: [
|
||||||
|
'app/bower_components/angular/angular.js',
|
||||||
|
'app/bower_components/angular-mocks/angular-mocks.js',
|
||||||
|
'app/scripts/*.js',
|
||||||
|
'app/scripts/**/*.js',
|
||||||
|
'test/mock/**/*.js',
|
||||||
|
'test/spec/**/*.js'
|
||||||
|
],
|
||||||
|
|
||||||
|
// list of files / patterns to exclude
|
||||||
|
exclude: [],
|
||||||
|
|
||||||
|
// web server port
|
||||||
|
port: 8080,
|
||||||
|
|
||||||
|
// level of logging
|
||||||
|
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
|
||||||
|
|
||||||
|
// enable / disable watching file and executing tests whenever any file changes
|
||||||
|
autoWatch: false,
|
||||||
|
|
||||||
|
|
||||||
|
// Start these browsers, currently available:
|
||||||
|
// - Chrome
|
||||||
|
// - ChromeCanary
|
||||||
|
// - Firefox
|
||||||
|
// - Opera
|
||||||
|
// - Safari (only Mac)
|
||||||
|
// - PhantomJS
|
||||||
|
// - IE (only Windows)
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
|
|
||||||
|
// Continuous Integration mode
|
||||||
|
// if true, it capture browsers, run tests and exit
|
||||||
|
singleRun: false
|
||||||
|
});
|
||||||
|
};
|
38
mod/dashboard/package.json
Normal file
38
mod/dashboard/package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "etcd-dashboard",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"grunt": "~0.4.1",
|
||||||
|
"grunt-contrib-copy": "~0.4.1",
|
||||||
|
"grunt-contrib-concat": "~0.3.0",
|
||||||
|
"grunt-contrib-coffee": "~0.7.0",
|
||||||
|
"grunt-contrib-uglify": "~0.2.0",
|
||||||
|
"grunt-contrib-compass": "~0.5.0",
|
||||||
|
"grunt-contrib-jshint": "~0.6.0",
|
||||||
|
"grunt-contrib-cssmin": "~0.6.0",
|
||||||
|
"grunt-contrib-connect": "~0.3.0",
|
||||||
|
"grunt-contrib-clean": "~0.5.0",
|
||||||
|
"grunt-contrib-htmlmin": "~0.1.3",
|
||||||
|
"grunt-contrib-imagemin": "~0.2.0",
|
||||||
|
"grunt-contrib-watch": "~0.5.2",
|
||||||
|
"grunt-autoprefixer": "~0.2.0",
|
||||||
|
"grunt-usemin": "~0.1.11",
|
||||||
|
"grunt-svgmin": "~0.2.0",
|
||||||
|
"grunt-rev": "~0.1.0",
|
||||||
|
"grunt-open": "~0.2.0",
|
||||||
|
"grunt-concurrent": "~0.3.0",
|
||||||
|
"load-grunt-tasks": "~0.1.0",
|
||||||
|
"connect-livereload": "~0.2.0",
|
||||||
|
"grunt-google-cdn": "~0.2.0",
|
||||||
|
"grunt-ngmin": "~0.0.2",
|
||||||
|
"time-grunt": "~0.1.0",
|
||||||
|
"grunt-karma": "~0.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "grunt test"
|
||||||
|
}
|
||||||
|
}
|
11
mod/dashboard/resources/bindata-file.go
Normal file
11
mod/dashboard/resources/bindata-file.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package resources
|
||||||
|
|
||||||
|
func File(name string) ([]byte, bool) {
|
||||||
|
data, ok := go_bindata[name]
|
||||||
|
|
||||||
|
if ok == false {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return data(), true
|
||||||
|
}
|
7
mod/dashboard/resources/bindata-toc.go
Normal file
7
mod/dashboard/resources/bindata-toc.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package resources
|
||||||
|
|
||||||
|
// Global Table of Contents map. Generated by go-bindata.
|
||||||
|
// After startup of the program, all generated data files will
|
||||||
|
// put themselves in this map. The key is the full filename, as
|
||||||
|
// supplied to go-bindata.
|
||||||
|
var go_bindata = make(map[string]func() []byte)
|
39
mod/dashboard/resources/browser.html.go
Normal file
39
mod/dashboard/resources/browser.html.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package resources
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _browser_html = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x8c\x54\x4f\x6f\xdb\x3e\x0c\xbd\xf7\x53\xb0\xbe\xcb\x3e\xfc\xf0\x43\xdb\x41\x36\xd0\x0d\x3d\xf4\xb6\xcb\x80\x0d\x45\x51\x28\x12\x13\xab\xb3\x45\x4d\xa2\x93\xe5\xdb\x4f\xf2\x9f\xd6\x6d\xb3\x21\x39\xc4\xb4\xc8\xf7\x44\xf2\xbd\x44\x5e\x1a\xd2\x7c\xf4\x08\x2d\xf7\x5d\x73\x21\x2f\x85\x78\xb0\x5b\xe8\x18\xee\xef\xe0\xea\xb1\x81\xf1\x23\x73\x16\x74\xa7\x62\xac\x0b\x47\xe2\x39\xa6\x0a\x61\xf1\x66\x7a\x5c\x4f\x8f\xab\xa2\x01\x79\xf9\x80\xce\xd8\xed\xa3\x10\xaf\x6c\x6b\xaa\x33\xd8\xfe\x41\x73\x7d\x0e\xcd\xdf\xf0\x3b\x9e\x29\xf2\x41\x73\x02\x3f\x02\x85\x78\x03\xce\xf7\xa0\x32\x39\x48\x61\x8f\xac\x40\xb7\x2a\x44\xe4\xba\x18\x78\x2b\x52\xb7\xab\x54\xcb\xec\x05\xfe\x1a\xec\xbe\x2e\xbe\x8b\x6f\xb7\xe2\x0b\xf5\x5e\xb1\xdd\x74\x58\x80\x26\xc7\xe8\x12\xee\xfe\xae\x46\xb3\xc3\x05\xc9\x96\x3b\x6c\x90\xb5\x81\xcf\x81\x0e\x11\x83\xac\xa6\xb3\x15\xb3\x53\x3d\xd6\x85\xc1\xa8\x83\xf5\x6c\xc9\xad\xf8\x8a\x8f\x85\x7b\x8b\x07\x4f\x81\x57\x55\x07\x6b\xb8\xad\x0d\xee\xad\x46\x31\xbe\x2c\xb8\x34\x33\x7c\xed\x94\x46\xd8\xaa\x94\x25\x57\xa6\x2f\x50\xce\x80\xf2\xbe\x43\xc1\x34\xe8\x56\x8c\x09\xef\x76\x60\x1d\x70\x8b\x10\x88\x18\x8c\x0d\xa8\x99\xc2\x11\xf2\xb2\x2e\x5e\xb4\xe9\xac\xfb\x09\x01\xbb\xba\x88\x7c\xec\x30\xb6\x88\xa9\x97\x36\xe0\x76\x39\xa9\x7a\x65\x5d\xa9\x63\xda\xfa\x85\xac\x96\x1d\xcb\x0d\x99\x23\xb8\x9d\x48\x37\xd7\x45\xde\xc9\xbc\x92\x55\xaf\x6f\x0c\x3a\x5f\x29\xfd\xa2\xe4\x66\xac\x6f\x13\xfe\x58\x34\x3f\x68\x00\x15\x10\x86\x68\x53\xe3\xca\x81\x8c\x1c\xc8\xed\x1a\x1a\xd8\x28\x46\x23\xab\xf9\x00\x26\x5c\x28\xd3\x26\x50\x45\x04\xa9\xe6\x76\xb3\xa6\x9f\xaa\x6a\xc5\x5b\x6a\xea\xab\xa2\x19\xfc\x2e\x28\x83\x70\xa4\x21\x2c\x70\x59\xa9\x06\x98\xc0\xf6\x3e\xd0\x7e\xce\xe1\x6f\x8f\xc1\xa2\xd3\x58\xca\xca\x2f\x83\xac\x4c\x76\x62\xb4\x9b\xd7\xd1\x26\xc9\x21\x06\x9d\xa6\xa3\x03\x86\xa7\x74\xbf\x27\x97\x54\x8d\x15\xc6\xff\x45\x6c\x6d\xff\x12\x94\xd9\xc7\x69\xac\x11\x74\x1e\xc7\x73\x24\xf7\x5f\xd5\xd9\xcd\x14\x95\x7d\x12\xe6\x23\xcb\xe9\x8e\xe1\xd6\x98\x69\xca\x68\x19\x81\xc2\xe8\x19\xab\x55\xf6\xe8\x62\x3e\x68\x31\x89\x30\xfd\x9c\x12\xcc\xd8\x3d\x58\x93\xe5\x35\x4f\xf3\xde\x8a\xac\x79\x36\xed\x24\xfa\xa2\x76\x95\x4a\x9b\x57\x57\xad\xa7\x98\xe2\x38\xeb\x12\x44\x4f\x66\x48\xb6\x7a\xd7\xf8\x79\xd8\xf9\xfd\x1d\x56\x56\xd9\x8c\xa3\x3b\xf3\x9f\xe3\x9f\x00\x00\x00\xff\xff\x4a\x5c\x90\x9e\x2c\x05\x00\x00"
|
||||||
|
|
||||||
|
// browser_html returns raw, uncompressed file data.
|
||||||
|
func browser_html() []byte {
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_browser_html))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_browser_html)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("Decompression failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go_bindata["/browser.html"] = browser_html
|
||||||
|
}
|
39
mod/dashboard/resources/index.html.go
Normal file
39
mod/dashboard/resources/index.html.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/scripts-browser-modules.js.go
Normal file
39
mod/dashboard/resources/scripts-browser-modules.js.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/scripts-browser-scripts.js.go
Normal file
39
mod/dashboard/resources/scripts-browser-scripts.js.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/scripts-stats-modules.js.go
Normal file
39
mod/dashboard/resources/scripts-stats-modules.js.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/scripts-stats-scripts.js.go
Normal file
39
mod/dashboard/resources/scripts-stats-scripts.js.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/stats.html.go
Normal file
39
mod/dashboard/resources/stats.html.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package resources
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _stats_html = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x8c\x54\xcb\x6e\xdb\x30\x10\xbc\xe7\x2b\x36\xba\xd3\x3c\x14\x45\x92\x82\x12\x90\x16\x39\xe4\x56\xa0\x28\xd0\x22\x08\x02\x9a\x5c\x5b\x4c\x25\x2e\x4b\xae\xec\xfa\xef\x4b\x4a\x56\xa2\x3c\x5a\xd8\x07\x6b\xc5\xe5\xcc\x3e\x66\x6c\x75\x6e\xc9\xf0\x21\x20\xb4\xdc\x77\xcd\x99\x3a\x17\xe2\xce\x6d\xa0\x63\xb8\xbd\x81\x8b\xfb\x06\xc6\x8f\x2a\x59\x30\x9d\x4e\xa9\xae\x3c\x89\xc7\x94\x6f\x08\x87\x57\xd3\xe3\x72\x7a\x5c\x54\x0d\xa8\xf3\x3b\xf4\xd6\x6d\xee\x85\x78\x66\x5b\x52\x9d\xc0\xf6\x1f\x9a\xcb\x53\x68\xfe\x85\xdf\xf2\x91\xa2\x1c\x34\xef\xe0\x47\xa0\x10\x2f\xc0\xa5\x0e\x6a\x5b\x82\x1c\xf6\xc8\x1a\x4c\xab\x63\x42\xae\xab\x81\x37\x22\x77\xbb\x48\xb5\xcc\x41\xe0\xef\xc1\xed\xea\xea\x87\xf8\x7e\x2d\xbe\x50\x1f\x34\xbb\x75\x87\x15\x18\xf2\x8c\x3e\xe3\x6e\x6f\x6a\xb4\x5b\x9c\x91\xec\xb8\xc3\x06\xd9\x58\xf8\x1c\x69\x9f\x30\x2a\x39\x9d\x2d\x98\xbd\xee\xb1\xae\x2c\x26\x13\x5d\x60\x47\x7e\xc1\x57\xbd\xbd\xb8\x73\xb8\x0f\x14\x79\x71\x6b\xef\x2c\xb7\xb5\xc5\x9d\x33\x28\xc6\x97\x19\x97\x67\x86\xaf\x9d\x36\x08\x1b\x9d\xb3\xe4\x57\xf9\x0b\xb4\xb7\xa0\x43\xe8\x50\x30\x0d\xa6\x15\x63\x22\xf8\x2d\x38\x0f\xdc\x22\x44\x22\x06\xeb\x22\x1a\xa6\x78\x80\xb2\xac\xb3\x27\x6d\x3a\xe7\x7f\x41\xc4\xae\xae\x12\x1f\x3a\x4c\x2d\x62\xee\xa5\x8d\xb8\x99\x4f\x64\xaf\x9d\x5f\x99\x94\xb7\x7e\xa6\xe4\xbc\x63\xb5\x26\x7b\x00\xbf\x15\xb9\x72\x5d\x95\x9d\x7c\x63\xcd\x69\xd1\xe9\x0b\x7b\x1e\x0b\xaa\x30\xeb\xb8\x1e\x17\xd8\x66\xf4\xa1\x6a\x7e\xd2\x00\x3a\x22\x0c\xc9\xe5\xb6\xb5\x07\x95\x38\x92\xdf\x36\x34\xb0\xd5\x8c\x56\xc9\xe3\x01\x4c\xb8\xb8\xca\x7b\x40\x9d\x10\x94\x3e\x36\x5b\x14\xfd\x24\xe5\x82\x77\x65\xa8\x97\x55\x33\x84\x6d\xd4\x16\xe1\x40\x43\x9c\xe1\x4a\xea\x06\x98\xc0\xf5\x21\xd2\xee\x98\xc3\x3f\x01\xa3\x43\x6f\x70\xa5\x64\x98\x07\x59\x58\xec\x9d\xd1\xae\x9e\x47\x9b\x04\x87\x14\x4d\x9e\x8e\xf6\x18\x1f\x72\xfd\x40\x3e\x6b\x9a\x24\xa6\x8f\x22\xb5\xae\x7f\x0a\x56\xc5\xc5\x79\xac\x11\x74\x1a\xc7\x63\x22\xff\x41\x76\x6e\x3d\x45\xab\x3e\xcb\xf2\x96\xe5\xfd\x8e\xe1\xda\xda\x69\xca\xe4\x18\x81\xe2\xe8\x18\x67\x74\x71\xe8\x6c\x3d\x68\x31\x8b\x30\xfd\x98\x32\xcc\xba\x1d\x38\x5b\xc4\xb5\x0f\x69\x14\xb7\xe8\x5d\x0c\x3b\x09\x3e\x6b\x2d\xf3\xc5\xe6\xd9\x51\xcb\x19\xa6\x38\xc9\x11\x2e\x7a\xb2\x43\x36\xd4\xab\xa6\x4f\x41\x1e\xdf\x5e\x21\x95\x2c\x26\x1c\x5d\x59\xfe\x14\xff\x06\x00\x00\xff\xff\x95\x89\x83\x4d\x24\x05\x00\x00"
|
||||||
|
|
||||||
|
// stats_html returns raw, uncompressed file data.
|
||||||
|
func stats_html() []byte {
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_stats_html))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_stats_html)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("Decompression failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go_bindata["/stats.html"] = stats_html
|
||||||
|
}
|
39
mod/dashboard/resources/styles-main.css.go
Normal file
39
mod/dashboard/resources/styles-main.css.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/views-browser.html.go
Normal file
39
mod/dashboard/resources/views-browser.html.go
Normal file
File diff suppressed because one or more lines are too long
39
mod/dashboard/resources/views-stats.html.go
Normal file
39
mod/dashboard/resources/views-stats.html.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package resources
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _views_stats_html = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xd4\x56\x4b\x8f\xdb\x36\x10\xbe\xe7\x57\x10\xec\xc1\x09\x20\xc9\x7a\xf8\xb1\x32\x2c\x03\x4d\x0b\xf4\xd2\xed\xa1\x29\x0a\x14\x45\x0f\x34\x45\x5b\xc4\xd2\x94\x4a\xd2\xd6\x3a\xae\xff\x7b\x87\xa2\xec\xae\x64\xc5\xdd\xa0\x7b\xe9\x3e\x40\x71\x38\xf3\xcd\xa7\xe1\xc7\xa1\x96\x39\x3f\x20\x2a\x88\xd6\x19\x66\x86\xe6\x3e\x2d\xa5\x21\x5c\x32\x85\x9a\xa9\x36\xc4\x68\x74\x3a\xd1\x52\xec\x77\x52\x9f\xcf\xf0\x6c\xc8\x5a\xb0\x5f\xb9\xe6\x6b\x2e\xb8\x39\x9e\xcf\x78\xf5\x0e\xc1\xcf\x0d\xd6\xba\xcc\x8f\xed\xda\xe0\xfa\xa6\x54\x3b\x62\x7c\xcd\x04\xa3\xa6\x54\x2f\x5c\x07\xdd\x2f\x7e\x3e\x37\x6c\x87\xba\xa6\xad\x22\x55\x81\x91\xdc\xfa\x54\x70\xfa\x94\x61\x5d\x94\xf5\x0f\xd6\xf8\xfe\x43\x0f\xb7\xc1\xd6\x87\x2d\x3a\x30\xa5\x79\x29\x33\x1c\x05\x11\x46\xcf\x3b\x21\x21\x53\x61\x4c\xb5\x18\x8f\xeb\xba\x0e\xea\x24\x28\xd5\x76\x1c\x87\x61\x38\x06\xff\xd6\x65\xf1\x2c\xb8\x7c\x1a\x72\x8c\xd2\x34\x1d\x37\xab\xe0\x9a\xe1\xb0\x7a\xc6\xe8\xe8\xc6\x1b\x02\x95\x62\x9a\xa9\x03\xfb\x56\x57\xf0\x02\x3f\x13\xc3\xcb\x0c\x3f\x3f\x72\xf9\x1b\xfc\x63\x74\xe0\xac\xfe\x58\x5a\x10\x14\xa2\x99\xfd\x0b\xc2\x70\x8e\x11\x93\xb6\xf6\xfe\x9a\xd0\xa7\xad\x2a\xf7\x32\xcf\xb0\x64\x35\xea\x79\x01\xcf\x85\xae\x08\x65\x19\xbe\xe4\x19\xaa\x41\x45\x4c\x81\x36\x5c\x88\x0c\x7f\x13\x7d\x0f\xbf\x1f\x31\x02\xc0\xc7\x24\xf4\x01\x28\x8d\xa8\x1f\xcd\x82\x59\x1c\x79\xa1\x9f\x58\xc3\xc4\x8b\x92\x60\x32\x9f\x5c\x66\x6e\xa0\xa1\xd7\xba\xb9\x55\xaf\xb3\xda\x0e\x37\xc9\xb5\xb3\xfb\x9d\x98\x16\xf8\xbb\xd9\x35\x5b\xf2\x90\x78\x93\x06\xdd\x51\xf2\x2e\xdc\x3e\x23\xa0\xe9\x4d\xe6\x41\x9c\xa4\xd4\x4f\x83\x69\x94\x02\xcd\xc8\xce\xa7\xfe\x3c\x98\x47\xb3\xcb\xc4\x0d\x37\x04\x3e\xc5\x61\x30\x79\x00\xd2\x71\x30\x9f\x3d\x00\x6e\xfb\x44\x5b\x2c\xcf\xc5\x79\x0d\xd6\x65\xe2\x86\x4f\x89\xf3\x71\xd9\xbd\x2b\x8f\xcf\x78\x3c\x50\x65\x2b\x9d\xd5\xbb\xae\xb2\xc7\x20\xed\xff\x20\xf6\xe6\x00\xf6\xc5\xfe\x8b\x35\xfe\xff\xc5\x3e\x4d\x83\x49\x3a\xb3\xc3\x34\x4c\xee\x0a\xbe\xe7\xf9\x4a\xd1\x2b\x60\xd0\x10\x76\x74\x71\xff\x00\xd4\x3c\x37\x45\x86\x1d\x38\x46\x05\xe3\xdb\xc2\x40\xd5\x92\x20\x8a\xa2\xc1\x0d\xee\x22\xc6\xe0\x68\x03\xdf\x18\x16\x0e\x41\x92\xc6\x6f\x00\xeb\xe4\x78\x47\x8d\xfd\x69\x5f\x98\xae\xd3\xf6\x20\x8a\x78\xf5\x48\x68\x01\xf7\x06\xfa\x91\x18\x26\xe9\x71\x39\x06\xdb\x7d\x89\x37\x48\xff\x5c\x38\x18\x71\xd8\x5d\xe1\xc2\xfb\x09\xbe\x8e\xa3\xe0\xda\xdc\xa3\x08\xcb\x03\xfc\x9a\x63\x85\x28\x13\xa2\x22\x79\xce\xe5\xb6\x29\xbe\x9d\x5b\x5d\xb5\xf3\x7e\x4c\xc1\x48\x3e\x50\x66\x93\x77\x08\x49\xb2\x63\xbe\x75\x85\xd7\xbc\xd2\xf8\x09\x8c\xcb\xb1\x79\x45\xf8\xb5\x28\xd7\xe2\xf6\xa3\xc0\x72\xcb\x64\x69\xec\x0d\x3c\x04\xaf\x6c\xf3\x50\xac\x62\x04\xd4\xb2\x6b\xf9\x70\x89\xda\x47\x3d\x70\x72\x2e\xbc\x20\x50\xd7\xdc\xd0\x02\xd9\x6e\x72\x32\x6a\xcf\x16\x23\xd1\xbc\xda\xc8\x43\x1b\x22\x34\x5b\xa0\xd1\xa6\x14\xa2\xac\xc1\x74\xfe\xdd\xad\xd9\x97\x45\x59\x76\xc9\x10\xd8\x8a\xfc\xf1\x85\x34\x4d\x2a\xbb\xa7\xd7\x5c\x7e\x5d\x30\xc8\x26\xda\x0a\x9e\x4e\x2f\x61\xce\xe7\x25\x6c\x90\xec\x54\xac\x5d\xf7\xcd\xb1\x82\x36\xf0\xde\x05\x7e\x00\xf5\x83\xe3\x6a\xa0\xff\xde\x49\x9d\xb3\x0d\xd9\x0b\x73\x9b\xf4\xcb\x28\xc3\xbb\xda\x56\xf0\x5f\x12\x77\x2e\x81\x3f\xf7\x44\x31\xd7\xe8\xc1\xba\x40\xa7\xd1\x0b\x3b\x1c\x20\xc6\xe4\x68\x71\xad\x69\x2b\x93\x80\xee\x95\x62\xd2\xa0\x25\x8a\xa7\x1e\xea\x84\x94\x8a\xc8\x2d\xbb\x1b\x33\x0b\x7b\x31\x8a\xe5\x77\x02\x56\x19\x44\xc0\x27\xe0\x6b\x8a\x3a\xa0\x69\xff\x40\xc4\x9e\xbd\xdc\xd2\x7e\x82\xbf\x90\xdc\xef\xd6\x4c\x2d\x22\x04\x9f\x9f\x3b\xfd\xd5\x75\x07\xab\xba\x39\x2d\xb7\x27\x03\x8c\xb6\x01\x0c\x76\x99\xf6\xb1\x1d\xfe\x0e\x00\x00\xff\xff\xe8\x36\xb1\x2a\x35\x0b\x00\x00"
|
||||||
|
|
||||||
|
// views_stats_html returns raw, uncompressed file data.
|
||||||
|
func views_stats_html() []byte {
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_views_stats_html))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_views_stats_html)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("Decompression failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go_bindata["/views/stats.html"] = views_stats_html
|
||||||
|
}
|
35
mod/dashboard/test/.jshintrc
Normal file
35
mod/dashboard/test/.jshintrc
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"node": true,
|
||||||
|
"browser": true,
|
||||||
|
"esnext": true,
|
||||||
|
"bitwise": true,
|
||||||
|
"camelcase": true,
|
||||||
|
"curly": true,
|
||||||
|
"eqeqeq": true,
|
||||||
|
"immed": true,
|
||||||
|
"indent": 2,
|
||||||
|
"latedef": true,
|
||||||
|
"newcap": true,
|
||||||
|
"noarg": true,
|
||||||
|
"quotmark": "single",
|
||||||
|
"regexp": true,
|
||||||
|
"undef": true,
|
||||||
|
"unused": true,
|
||||||
|
"strict": true,
|
||||||
|
"trailing": true,
|
||||||
|
"smarttabs": true,
|
||||||
|
"globals": {
|
||||||
|
"after": false,
|
||||||
|
"afterEach": false,
|
||||||
|
"angular": false,
|
||||||
|
"before": false,
|
||||||
|
"beforeEach": false,
|
||||||
|
"browser": false,
|
||||||
|
"describe": false,
|
||||||
|
"expect": false,
|
||||||
|
"inject": false,
|
||||||
|
"it": false,
|
||||||
|
"spyOn": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
10
mod/dashboard/test/runner.html
Normal file
10
mod/dashboard/test/runner.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>End2end Test Runner</title>
|
||||||
|
<script src="vendor/angular-scenario.js" ng-autotest></script>
|
||||||
|
<script src="scenarios.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
22
mod/dashboard/test/spec/controllers/main.js
Normal file
22
mod/dashboard/test/spec/controllers/main.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('Controller: MainCtrl', function () {
|
||||||
|
|
||||||
|
// load the controller's module
|
||||||
|
beforeEach(module('etcdDashboardApp'));
|
||||||
|
|
||||||
|
var MainCtrl,
|
||||||
|
scope;
|
||||||
|
|
||||||
|
// Initialize the controller and a mock scope
|
||||||
|
beforeEach(inject(function ($controller, $rootScope) {
|
||||||
|
scope = $rootScope.$new();
|
||||||
|
MainCtrl = $controller('MainCtrl', {
|
||||||
|
$scope: scope
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should attach a list of awesomeThings to the scope', function () {
|
||||||
|
expect(scope.awesomeThings.length).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
18
mod/mod.go
Normal file
18
mod/mod.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// mod is the entry point to all of the etcd modules.
|
||||||
|
package mod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/mod/dashboard"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ServeMux *http.Handler
|
||||||
|
|
||||||
|
func HttpHandler() (handler http.Handler) {
|
||||||
|
modMux := mux.NewRouter()
|
||||||
|
modMux.PathPrefix("/dashboard/").
|
||||||
|
Handler(http.StripPrefix("/dashboard/", dashboard.HttpHandler()))
|
||||||
|
return modMux
|
||||||
|
}
|
39
scripts/build-release
Executable file
39
scripts/build-release
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
VER=$1
|
||||||
|
|
||||||
|
function build {
|
||||||
|
proj=${1}
|
||||||
|
ver=${2}
|
||||||
|
|
||||||
|
if [ ! -d ${proj} ]; then
|
||||||
|
git clone https://github.com/coreos/${proj}
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ${proj}
|
||||||
|
git checkout master
|
||||||
|
git fetch --all
|
||||||
|
git reset --hard origin/master
|
||||||
|
git checkout $ver
|
||||||
|
./build
|
||||||
|
cd -
|
||||||
|
}
|
||||||
|
|
||||||
|
function package {
|
||||||
|
proj=${1}
|
||||||
|
target=${2}
|
||||||
|
|
||||||
|
cp ${proj}/${proj} ${target}
|
||||||
|
cp ${proj}/README.md ${target}/README-${proj}.md
|
||||||
|
}
|
||||||
|
|
||||||
|
build etcd ${VER}
|
||||||
|
build etcdctl ${VER}
|
||||||
|
|
||||||
|
TARGET="etcd-$VER-$(uname -s)-$(uname -m)"
|
||||||
|
mkdir ${TARGET}
|
||||||
|
|
||||||
|
package etcd ${TARGET}
|
||||||
|
package etcdctl ${TARGET}
|
||||||
|
|
||||||
|
tar cvvfz ${TARGET}.tar.gz ${TARGET}
|
@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
etcdErr "github.com/coreos/etcd/error"
|
etcdErr "github.com/coreos/etcd/error"
|
||||||
"github.com/coreos/etcd/log"
|
"github.com/coreos/etcd/log"
|
||||||
|
"github.com/coreos/etcd/mod"
|
||||||
"github.com/coreos/etcd/server/v1"
|
"github.com/coreos/etcd/server/v1"
|
||||||
"github.com/coreos/etcd/server/v2"
|
"github.com/coreos/etcd/server/v2"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
@ -55,6 +56,7 @@ func New(name string, urlStr string, listenHost string, tlsConf *TLSConfig, tlsI
|
|||||||
s.handleFunc("/version", s.GetVersionHandler).Methods("GET")
|
s.handleFunc("/version", s.GetVersionHandler).Methods("GET")
|
||||||
s.installV1()
|
s.installV1()
|
||||||
s.installV2()
|
s.installV2()
|
||||||
|
s.installMod()
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -119,6 +121,11 @@ func (s *Server) installV2() {
|
|||||||
s.handleFunc("/v2/speedTest", s.SpeedTestHandler).Methods("GET")
|
s.handleFunc("/v2/speedTest", s.SpeedTestHandler).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) installMod() {
|
||||||
|
r := s.Handler.(*mux.Router)
|
||||||
|
r.PathPrefix("/etcd/mod").Handler(http.StripPrefix("/etcd/mod", mod.HttpHandler()))
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a v1 server handler to the router.
|
// Adds a v1 server handler to the router.
|
||||||
func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route {
|
func (s *Server) handleFuncV1(path string, f func(http.ResponseWriter, *http.Request, v1.Server) error) *mux.Route {
|
||||||
return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error {
|
return s.handleFunc(path, func(w http.ResponseWriter, req *http.Request) error {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2013 CoreOS Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
1
third_party/deps
vendored
1
third_party/deps
vendored
@ -11,4 +11,5 @@ packages="
|
|||||||
bitbucket.org/kardianos/osext
|
bitbucket.org/kardianos/osext
|
||||||
code.google.com/p/go.net
|
code.google.com/p/go.net
|
||||||
code.google.com/p/goprotobuf
|
code.google.com/p/goprotobuf
|
||||||
|
github.com/jteeuwen/go-bindata
|
||||||
"
|
"
|
||||||
|
10
third_party/github.com/jteeuwen/go-bindata/CONTRIBUTORS
vendored
Normal file
10
third_party/github.com/jteeuwen/go-bindata/CONTRIBUTORS
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# This is the list of people who can contribute (or have contributed) to this
|
||||||
|
# project. This includes code, documentation, testing, content creation and
|
||||||
|
# bugfixes.
|
||||||
|
#
|
||||||
|
# Names should be added to this file like so:
|
||||||
|
# Name [<email address>]
|
||||||
|
#
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Jim Teeuwen <jimteeuwen at gmail dot com>
|
3
third_party/github.com/jteeuwen/go-bindata/LICENSE
vendored
Normal file
3
third_party/github.com/jteeuwen/go-bindata/LICENSE
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
license. Its contents can be found at:
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0
|
173
third_party/github.com/jteeuwen/go-bindata/README.md
vendored
Normal file
173
third_party/github.com/jteeuwen/go-bindata/README.md
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
## bindata
|
||||||
|
|
||||||
|
This tool converts any file into managable Go source code. Useful for embedding
|
||||||
|
binary data into a go program. The file data is optionally gzip compressed
|
||||||
|
before being converted to a raw byte slice.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The simplest invocation is to pass it only the input file name.
|
||||||
|
The output file and code settings are inferred from this automatically.
|
||||||
|
|
||||||
|
$ go-bindata testdata/gophercolor.png
|
||||||
|
[w] No output file specified. Using 'testdata/gophercolor.png.go'.
|
||||||
|
[w] No package name specified. Using 'main'.
|
||||||
|
[w] No function name specified. Using 'testdata_gophercolor_png'.
|
||||||
|
|
||||||
|
This creates the `testdata/gophercolor.png.go` file which has a package
|
||||||
|
declaration with name `main` and one function named `testdata_gophercolor_png` with
|
||||||
|
the following signature:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func testdata_gophercolor_png() []byte
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now simply include the new .go file in your program and call
|
||||||
|
`testdata_gophercolor_png()` to get the (uncompressed) image data. The function panics
|
||||||
|
if something went wrong during decompression. See the testdata directory for
|
||||||
|
example input and output files for various modes.
|
||||||
|
|
||||||
|
Aternatively, you can pipe the input file data into stdin. `go-bindata` will
|
||||||
|
then spit out the generated Go code to stdout. This does require explicitly
|
||||||
|
naming the desired function name, as it can not be inferred from the
|
||||||
|
input data. The package name will still default to 'main'.
|
||||||
|
|
||||||
|
$ cat testdata/gophercolor.png | go-bindata -f gophercolor_png | gofmt
|
||||||
|
|
||||||
|
Invoke the program with the `-h` flag for more options.
|
||||||
|
|
||||||
|
In order to strip off a part of the generated function name, we can use the `-prefix` flag.
|
||||||
|
In the above example, the input file `testdata/gophercolor.png` yields a function named
|
||||||
|
`testdata_gophercolor_png`. If we want the `testdata` component to be left out, we invoke
|
||||||
|
the program as follows:
|
||||||
|
|
||||||
|
$ go-bindata -prefix "testdata/" testdata/gophercolor.png
|
||||||
|
|
||||||
|
|
||||||
|
### Lower memory footprint
|
||||||
|
|
||||||
|
Using the `-nomemcopy` flag, will alter the way the output file is generated.
|
||||||
|
It will employ a hack that allows us to read the file data directly from
|
||||||
|
the compiled program's `.rodata` section. This ensures that when we call
|
||||||
|
call our generated function, we omit unnecessary memcopies.
|
||||||
|
|
||||||
|
The downside of this, is that it requires dependencies on the `reflect` and
|
||||||
|
`unsafe` packages. These may be restricted on platforms like AppEngine and
|
||||||
|
thus prevent you from using this mode.
|
||||||
|
|
||||||
|
Another disadvantage is that the byte slice we create, is strictly read-only.
|
||||||
|
For most use-cases this is not a problem, but if you ever try to alter the
|
||||||
|
returned byte slice, a runtime panic is thrown. Use this mode only on target
|
||||||
|
platforms where memory constraints are an issue.
|
||||||
|
|
||||||
|
The default behaviour is to use the old code generation method. This
|
||||||
|
prevents the two previously mentioned issues, but will employ at least one
|
||||||
|
extra memcopy and thus increase memory requirements.
|
||||||
|
|
||||||
|
For instance, consider the following two examples:
|
||||||
|
|
||||||
|
This would be the default mode, using an extra memcopy but gives a safe
|
||||||
|
implementation without dependencies on `reflect` and `unsafe`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func myfile() []byte {
|
||||||
|
return []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the same functionality, but uses the `.rodata` hack.
|
||||||
|
The byte slice returned from this example can not be written to without
|
||||||
|
generating a runtime error.
|
||||||
|
|
||||||
|
```go
|
||||||
|
var _myfile = "\x89\x50\x4e\x47\x0d\x0a\x1a"
|
||||||
|
|
||||||
|
func myfile() []byte {
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_myfile))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_myfile)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Optional compression
|
||||||
|
|
||||||
|
When the `-uncompressed` flag is given, the supplied resource is *not* GZIP compressed
|
||||||
|
before being turned into Go code. The data should still be accessed through
|
||||||
|
a function call, so nothing changes in the usage of the generated file.
|
||||||
|
|
||||||
|
This feature is useful if you do not care for compression, or the supplied
|
||||||
|
resource is already compressed. Doing it again would not add any value and may
|
||||||
|
even increase the size of the data.
|
||||||
|
|
||||||
|
The default behaviour of the program is to use compression.
|
||||||
|
|
||||||
|
|
||||||
|
### Table of Contents
|
||||||
|
|
||||||
|
With the `-toc` flag, we can have `go-bindata` create a table of contents for all the files
|
||||||
|
which have been generated by the tool. It does this by first generating a new file named
|
||||||
|
`bindata-toc.go`. This contains a global map of type `map[string] func() []byte`. It uses the
|
||||||
|
input filename as the key and the data function as the value. We can use this
|
||||||
|
to fetch all data for our files, matching a given pattern.
|
||||||
|
|
||||||
|
It then appands an `init` function to each generated file, which simply makes the data
|
||||||
|
function append itself to the global `bindata` map.
|
||||||
|
|
||||||
|
Once you have compiled your program with all these new files and run it, the map will
|
||||||
|
be populated by all generated data files.
|
||||||
|
|
||||||
|
**Note**: The `bindata-toc.go` file will not be created when we run in `pipe` mode.
|
||||||
|
The reason being, that the tool does not write any files at all, as it has no idea
|
||||||
|
where to save them. The data file is written to `stdout` instead after all.
|
||||||
|
|
||||||
|
|
||||||
|
#### Table of Contents keys
|
||||||
|
|
||||||
|
The keys used in the `go_bindata` map, are the same as the input file name passed to `go-bindata`.
|
||||||
|
This includes the fully qualified (absolute) path. In most cases, this is not desireable, as it
|
||||||
|
puts potentially sensitive information in your code base. For this purpose, the tool supplies
|
||||||
|
another command line flag `-prefix`. This accepts a portion of a path name, which should be
|
||||||
|
stripped off from the map keys and function names.
|
||||||
|
|
||||||
|
For example, running without the `-prefix` flag, we get:
|
||||||
|
|
||||||
|
$ go-bindata /path/to/templates/foo.html
|
||||||
|
|
||||||
|
go_bindata["/path/to/templates/foo.html"] = path_to_templates_foo_html
|
||||||
|
|
||||||
|
Running with the `-prefix` flag, we get:
|
||||||
|
|
||||||
|
$ go-bindata -prefix "/path/to/" /path/to/templates/foo.html
|
||||||
|
|
||||||
|
go_bindata["templates/foo.html"] = templates_foo_html
|
||||||
|
|
||||||
|
|
||||||
|
#### bindata-toc.go
|
||||||
|
|
||||||
|
The `bindata-toc.go` file is very simple and looks as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package $PACKAGENAME
|
||||||
|
|
||||||
|
// Global Table of Contents map. Generated by go-bindata.
|
||||||
|
// After startup of the program, all generated data files will
|
||||||
|
// put themselves in this map. The key is the full filename, as
|
||||||
|
// supplied to go-bindata.
|
||||||
|
var go_bindata = make(map[string] func() []byte)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Build tags
|
||||||
|
|
||||||
|
With the optional -tags flag, you can specify any go build tags that
|
||||||
|
must be fulfilled for the output file to be included in a build. This
|
||||||
|
is useful for including binary data in multiple formats, where the desired
|
||||||
|
format is specified at build time with the appropriate tag(s).
|
||||||
|
|
||||||
|
The tags are appended to a `// +build` line in the beginning of the output file
|
||||||
|
and must follow the build tags syntax specified by the go tool.
|
37
third_party/github.com/jteeuwen/go-bindata/lib/bytewriter.go
vendored
Normal file
37
third_party/github.com/jteeuwen/go-bindata/lib/bytewriter.go
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package bindata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var newline = []byte{'\n'}
|
||||||
|
|
||||||
|
type ByteWriter struct {
|
||||||
|
io.Writer
|
||||||
|
c int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ByteWriter) Write(p []byte) (n int, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for n = range p {
|
||||||
|
if w.c%12 == 0 {
|
||||||
|
w.Writer.Write(newline)
|
||||||
|
w.c = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w.Writer, "0x%02x,", p[n])
|
||||||
|
w.c++
|
||||||
|
}
|
||||||
|
|
||||||
|
n++
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
30
third_party/github.com/jteeuwen/go-bindata/lib/stringwriter.go
vendored
Normal file
30
third_party/github.com/jteeuwen/go-bindata/lib/stringwriter.go
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package bindata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StringWriter struct {
|
||||||
|
io.Writer
|
||||||
|
c int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *StringWriter) Write(p []byte) (n int, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for n = range p {
|
||||||
|
fmt.Fprintf(w.Writer, "\\x%02x", p[n])
|
||||||
|
w.c++
|
||||||
|
}
|
||||||
|
|
||||||
|
n++
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
39
third_party/github.com/jteeuwen/go-bindata/lib/toc.go
vendored
Normal file
39
third_party/github.com/jteeuwen/go-bindata/lib/toc.go
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package bindata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// createTOC writes a table of contents file to the given location.
|
||||||
|
func CreateTOC(dir, pkgname string) error {
|
||||||
|
file := filepath.Join(dir, "bindata-toc.go")
|
||||||
|
code := fmt.Sprintf(`package %s
|
||||||
|
|
||||||
|
// Global Table of Contents map. Generated by go-bindata.
|
||||||
|
// After startup of the program, all generated data files will
|
||||||
|
// put themselves in this map. The key is the full filename, as
|
||||||
|
// supplied to go-bindata.
|
||||||
|
var go_bindata = make(map[string]func() []byte)`, pkgname)
|
||||||
|
|
||||||
|
return ioutil.WriteFile(file, []byte(code), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTOCInit writes the TOC init function for a given data file
|
||||||
|
// replacing the prefix in the filename by "", funcname being the translated function name
|
||||||
|
func WriteTOCInit(output io.Writer, filename, prefix, funcname string) {
|
||||||
|
filename = strings.Replace(filename, prefix, "", 1)
|
||||||
|
fmt.Fprintf(output, `
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go_bindata[%q] = %s
|
||||||
|
}
|
||||||
|
`, filename, funcname)
|
||||||
|
}
|
187
third_party/github.com/jteeuwen/go-bindata/lib/translate.go
vendored
Normal file
187
third_party/github.com/jteeuwen/go-bindata/lib/translate.go
vendored
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package bindata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var regFuncName = regexp.MustCompile(`[^a-zA-Z0-9_]`)
|
||||||
|
|
||||||
|
// translate translates the input file to go source code.
|
||||||
|
func Translate(input io.Reader, output io.Writer, pkgname, funcname string, uncompressed, nomemcpy bool) {
|
||||||
|
if nomemcpy {
|
||||||
|
if uncompressed {
|
||||||
|
translate_nomemcpy_uncomp(input, output, pkgname, funcname)
|
||||||
|
} else {
|
||||||
|
translate_nomemcpy_comp(input, output, pkgname, funcname)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if uncompressed {
|
||||||
|
translate_memcpy_uncomp(input, output, pkgname, funcname)
|
||||||
|
} else {
|
||||||
|
translate_memcpy_comp(input, output, pkgname, funcname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// input -> gzip -> gowriter -> output.
|
||||||
|
func translate_memcpy_comp(input io.Reader, output io.Writer, pkgname, funcname string) {
|
||||||
|
fmt.Fprintf(output, `package %s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// %s returns raw, uncompressed file data.
|
||||||
|
func %s() []byte {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{`, pkgname, funcname, funcname)
|
||||||
|
|
||||||
|
gz := gzip.NewWriter(&ByteWriter{Writer: output})
|
||||||
|
io.Copy(gz, input)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
fmt.Fprint(output, `
|
||||||
|
}))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("Decompression failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
io.Copy(&b, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
return b.Bytes()
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// input -> gzip -> gowriter -> output.
|
||||||
|
func translate_memcpy_uncomp(input io.Reader, output io.Writer, pkgname, funcname string) {
|
||||||
|
fmt.Fprintf(output, `package %s
|
||||||
|
|
||||||
|
// %s returns raw file data.
|
||||||
|
func %s() []byte {
|
||||||
|
return []byte{`, pkgname, funcname, funcname)
|
||||||
|
|
||||||
|
io.Copy(&ByteWriter{Writer: output}, input)
|
||||||
|
|
||||||
|
fmt.Fprint(output, `
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// input -> gzip -> gowriter -> output.
|
||||||
|
func translate_nomemcpy_comp(input io.Reader, output io.Writer, pkgname, funcname string) {
|
||||||
|
fmt.Fprintf(output, `package %s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _%s = "`, pkgname, funcname)
|
||||||
|
|
||||||
|
gz := gzip.NewWriter(&StringWriter{Writer: output})
|
||||||
|
io.Copy(gz, input)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
fmt.Fprintf(output, `"
|
||||||
|
|
||||||
|
// %s returns raw, uncompressed file data.
|
||||||
|
func %s() []byte {
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_%s))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_%s)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic("Decompression failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, gz)
|
||||||
|
gz.Close()
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
`, funcname, funcname, funcname, funcname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// input -> gowriter -> output.
|
||||||
|
func translate_nomemcpy_uncomp(input io.Reader, output io.Writer, pkgname, funcname string) {
|
||||||
|
fmt.Fprintf(output, `package %s
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _%s = "`, pkgname, funcname)
|
||||||
|
|
||||||
|
io.Copy(&StringWriter{Writer: output}, input)
|
||||||
|
|
||||||
|
fmt.Fprintf(output, `"
|
||||||
|
|
||||||
|
// %s returns raw file data.
|
||||||
|
//
|
||||||
|
// WARNING: The returned byte slice is READ-ONLY.
|
||||||
|
// Attempting to alter the slice contents will yield a runtime panic.
|
||||||
|
func %s() []byte {
|
||||||
|
var empty [0]byte
|
||||||
|
sx := (*reflect.StringHeader)(unsafe.Pointer(&_%s))
|
||||||
|
b := empty[:]
|
||||||
|
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bx.Data = sx.Data
|
||||||
|
bx.Len = len(_%s)
|
||||||
|
bx.Cap = bx.Len
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
`, funcname, funcname, funcname, funcname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeFuncname creates a safe function name from the input path.
|
||||||
|
func SafeFuncname(in, prefix string) string {
|
||||||
|
name := strings.Replace(in, prefix, "", 1)
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
name = in
|
||||||
|
}
|
||||||
|
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
name = regFuncName.ReplaceAllString(name, "_")
|
||||||
|
|
||||||
|
if unicode.IsDigit(rune(name[0])) {
|
||||||
|
// Identifier can't start with a digit.
|
||||||
|
name = "_" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rid of "__" instances for niceness.
|
||||||
|
for strings.Index(name, "__") > -1 {
|
||||||
|
name = strings.Replace(name, "__", "_", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leading underscore is silly.
|
||||||
|
if name[0] == '_' {
|
||||||
|
name = name[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
31
third_party/github.com/jteeuwen/go-bindata/lib/version.go
vendored
Normal file
31
third_party/github.com/jteeuwen/go-bindata/lib/version.go
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package bindata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AppName = "bindata"
|
||||||
|
AppVersionMajor = 2
|
||||||
|
AppVersionMinor = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// revision part of the program version.
|
||||||
|
// This will be set automatically at build time like so:
|
||||||
|
//
|
||||||
|
// go build -ldflags "-X main.AppVersionRev `date -u +%s`"
|
||||||
|
var AppVersionRev string
|
||||||
|
|
||||||
|
func Version() string {
|
||||||
|
if len(AppVersionRev) == 0 {
|
||||||
|
AppVersionRev = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s %d.%d.%s (Go runtime %s).\nCopyright (c) 2010-2013, Jim Teeuwen.",
|
||||||
|
AppName, AppVersionMajor, AppVersionMinor, AppVersionRev, runtime.Version())
|
||||||
|
}
|
157
third_party/github.com/jteeuwen/go-bindata/main.go
vendored
Normal file
157
third_party/github.com/jteeuwen/go-bindata/main.go
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||||
|
// license. Its contents can be found at:
|
||||||
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/jteeuwen/go-bindata/lib"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pipe = false
|
||||||
|
in = ""
|
||||||
|
out = flag.String("out", "", "Optional path and name of the output file.")
|
||||||
|
pkgname = flag.String("pkg", "main", "Name of the package to generate.")
|
||||||
|
funcname = flag.String("func", "", "Optional name of the function to generate.")
|
||||||
|
prefix = flag.String("prefix", "", "Optional path prefix to strip off map keys and function names.")
|
||||||
|
uncompressed = flag.Bool("uncompressed", false, "The specified resource will /not/ be GZIP compressed when this flag is specified. This alters the generated output code.")
|
||||||
|
nomemcopy = flag.Bool("nomemcopy", false, "Use a .rodata hack to get rid of unnecessary memcopies. Refer to the documentation to see what implications this carries.")
|
||||||
|
tags = flag.String("tags", "", "Optional build tags")
|
||||||
|
toc = flag.Bool("toc", false, "Generate a table of contents for this and other files. The input filepath becomes the map key. This option is only useable in non-pipe mode.")
|
||||||
|
version = flag.Bool("version", false, "Display version information.")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
parseArgs()
|
||||||
|
|
||||||
|
if pipe {
|
||||||
|
bindata.Translate(os.Stdin, os.Stdout, *pkgname, *funcname, *uncompressed, *nomemcopy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fs, err := os.Open(in)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "[e] %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fs.Close()
|
||||||
|
|
||||||
|
fd, err := os.Create(*out)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "[e] %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
if *tags != "" {
|
||||||
|
fmt.Fprintf(fd, "// +build %s\n\n", *tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate binary to Go code.
|
||||||
|
bindata.Translate(fs, fd, *pkgname, *funcname, *uncompressed, *nomemcopy)
|
||||||
|
|
||||||
|
// Append the TOC init function to the end of the output file and
|
||||||
|
// write the `bindata-toc.go` file, if applicable.
|
||||||
|
if *toc {
|
||||||
|
dir, _ := filepath.Split(*out)
|
||||||
|
err := bindata.CreateTOC(dir, *pkgname)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "[e] %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bindata.WriteTOCInit(fd, in, *prefix, *funcname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseArgs processes and verifies commandline arguments.
|
||||||
|
func parseArgs() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Printf("Usage: %s [options] <filename>\n\n", os.Args[0])
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *version {
|
||||||
|
fmt.Printf("%s\n", bindata.Version())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe = flag.NArg() == 0
|
||||||
|
|
||||||
|
if !pipe {
|
||||||
|
*prefix, _ = filepath.Abs(filepath.Clean(*prefix))
|
||||||
|
in, _ = filepath.Abs(filepath.Clean(flag.Args()[0]))
|
||||||
|
*out = safeFilename(*out, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(*pkgname) == 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "[w] No package name specified. Using 'main'.")
|
||||||
|
*pkgname = "main"
|
||||||
|
} else {
|
||||||
|
if unicode.IsDigit(rune((*pkgname)[0])) {
|
||||||
|
// Identifier can't start with a digit.
|
||||||
|
*pkgname = "_" + *pkgname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(*funcname) == 0 {
|
||||||
|
if pipe {
|
||||||
|
// Can't infer from input file name in this mode.
|
||||||
|
fmt.Fprintln(os.Stderr, "[e] No function name specified.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
*funcname = bindata.SafeFuncname(in, *prefix)
|
||||||
|
fmt.Fprintf(os.Stderr, "[w] No function name specified. Using %s.\n", *funcname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeFilename creates a safe output filename from the given
|
||||||
|
// output and input paths.
|
||||||
|
func safeFilename(out, in string) string {
|
||||||
|
var filename string
|
||||||
|
|
||||||
|
if len(out) == 0 {
|
||||||
|
filename = in + ".go"
|
||||||
|
|
||||||
|
_, err := os.Lstat(filename)
|
||||||
|
if err == nil {
|
||||||
|
// File already exists. Pad name with a sequential number until we
|
||||||
|
// find a name that is available.
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
filename = path.Join(out, fmt.Sprintf("%s.%d.go", in, count))
|
||||||
|
_, err = os.Lstat(filename)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filename, _ = filepath.Abs(filepath.Clean(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure output directory exists while we're here.
|
||||||
|
dir, _ := filepath.Split(filename)
|
||||||
|
_, err := os.Lstat(dir)
|
||||||
|
if err != nil {
|
||||||
|
os.MkdirAll(dir, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename
|
||||||
|
}
|
7
third_party/github.com/jteeuwen/go-bindata/testdata/bindata-toc.go
vendored
Normal file
7
third_party/github.com/jteeuwen/go-bindata/testdata/bindata-toc.go
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Global Table of Contents map. Generated by go-bindata.
|
||||||
|
// After startup of the program, all generated data files will
|
||||||
|
// put themselves in this map. The key is the full filename, as
|
||||||
|
// supplied to go-bindata.
|
||||||
|
var go_bindata = make(map[string]func() []byte)
|
BIN
third_party/github.com/jteeuwen/go-bindata/testdata/gophercolor.png
vendored
Normal file
BIN
third_party/github.com/jteeuwen/go-bindata/testdata/gophercolor.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
1849
third_party/github.com/jteeuwen/go-bindata/testdata/memcpy-compressed.go
vendored
Normal file
1849
third_party/github.com/jteeuwen/go-bindata/testdata/memcpy-compressed.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1832
third_party/github.com/jteeuwen/go-bindata/testdata/memcpy-uncompressed.go
vendored
Normal file
1832
third_party/github.com/jteeuwen/go-bindata/testdata/memcpy-uncompressed.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1403
third_party/github.com/jteeuwen/go-bindata/testdata/nomemcpy-compressed.go
vendored
Normal file
1403
third_party/github.com/jteeuwen/go-bindata/testdata/nomemcpy-compressed.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1392
third_party/github.com/jteeuwen/go-bindata/testdata/nomemcpy-uncompressed.go
vendored
Normal file
1392
third_party/github.com/jteeuwen/go-bindata/testdata/nomemcpy-uncompressed.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1853
third_party/github.com/jteeuwen/go-bindata/testdata/toc.go
vendored
Normal file
1853
third_party/github.com/jteeuwen/go-bindata/testdata/toc.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user