From 9a58a1c2ce79d8605690310305d88d8447042114 Mon Sep 17 00:00:00 2001 From: Darius Tumas Date: Thu, 1 Dec 2016 13:12:34 +0200 Subject: [PATCH 01/10] Implemented support for custom servers --- README.md | 1 + docker-compose.env.example | 1 + roles/screeps-statsd/tasks/main.yml | 3 ++- src/ScreepsStatsd.coffee | 6 +++--- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9312a49..58bf5b1 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ You're now ready to run this whale of a command. Replace the stuff in caps with ``` ansible-playbook \ + -e screeps_hostname=https://screeps.com \ -e screeps_username=YOURUSERNAME \ -e screeps_password=YOURPASSWORD \ -e screeps_email=YOUREMAIL \ diff --git a/docker-compose.env.example b/docker-compose.env.example index 0f13245..bf31320 100644 --- a/docker-compose.env.example +++ b/docker-compose.env.example @@ -1,3 +1,4 @@ +SCREEPS_HOSTNAME=https://screeps.com SCREEPS_USERNAME=bkconrad SCREEPS_EMAIL=notreally@myemail.com SCREEPS_PASSWORD=definitelynotmypassword diff --git a/roles/screeps-statsd/tasks/main.yml b/roles/screeps-statsd/tasks/main.yml index 5d34b44..5613557 100644 --- a/roles/screeps-statsd/tasks/main.yml +++ b/roles/screeps-statsd/tasks/main.yml @@ -67,6 +67,7 @@ links: - graphite env: + SCREEPS_HOSTNAME: "{{ screeps_hostname }}" SCREEPS_USERNAME: "{{ screeps_username }}" SCREEPS_EMAIL: "{{ screeps_email }}" - SCREEPS_PASSWORD: "{{ screeps_password }}" \ No newline at end of file + SCREEPS_PASSWORD: "{{ screeps_password }}" diff --git a/src/ScreepsStatsd.coffee b/src/ScreepsStatsd.coffee index a47cb14..4465a8a 100644 --- a/src/ScreepsStatsd.coffee +++ b/src/ScreepsStatsd.coffee @@ -39,7 +39,7 @@ class ScreepsStatsd signin: () => @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR options = - uri: 'https://screeps.com/api/auth/signin' + uri: process.env.SCREEPS_HOSTNAME + '/api/auth/signin' json: true method: 'POST' body: @@ -51,8 +51,8 @@ class ScreepsStatsd getMemory: () => options = - uri: 'https://screeps.com/api/user/memory' - method: 'GET' + uri: process.env.SCREEPS_HOSTNAME + '/api/user/memory' + method: 'GET' json: true headers: "X-Token": @token From 009ca4098984af7a6c9647721bed1a5421c06aae Mon Sep 17 00:00:00 2001 From: Parakoopa Date: Sat, 10 Dec 2016 19:02:32 +0100 Subject: [PATCH 02/10] Support for alternative server sign-in using HTTP Basic Auth --- README.md | 9 +++++++++ docker-compose.env.example | 1 + src/ScreepsStatsd.coffee | 32 ++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9964877..a7c4d24 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ ansible-playbook \ -e screeps_username=YOURUSERNAME \ -e screeps_password=YOURPASSWORD \ -e screeps_email=YOUREMAIL \ + -e screeps_baisc_auth=0 \ --user ubuntu \ --private-key YOURPRIVATEKEY \ -i ,YOURSERVERIP \ @@ -90,6 +91,14 @@ to install the plugins, then `docker-compose restart grafana` to apply. Refresh your browser and voila! +## Advanced configuration + +### Alternative (Basic HTTP) Authentication +The value of `SCREEPS_BASIC_AUTH` determines how Grafana will try to login to the Screeps server. +If it's `0` the default mechanism used by the official server will be used. If it is `1` an alternative +mechanism is used, using HTTP Basic Authentication. This method is required by some private server mods +such as "screepsmod-auth". + ## License This software is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for more information. diff --git a/docker-compose.env.example b/docker-compose.env.example index bf31320..40fd01c 100644 --- a/docker-compose.env.example +++ b/docker-compose.env.example @@ -2,3 +2,4 @@ SCREEPS_HOSTNAME=https://screeps.com SCREEPS_USERNAME=bkconrad SCREEPS_EMAIL=notreally@myemail.com SCREEPS_PASSWORD=definitelynotmypassword +SCREEPS_BASIC_AUTH=0 diff --git a/src/ScreepsStatsd.coffee b/src/ScreepsStatsd.coffee index d5fe946..e7b1af9 100644 --- a/src/ScreepsStatsd.coffee +++ b/src/ScreepsStatsd.coffee @@ -43,6 +43,9 @@ class ScreepsStatsd if(token != "" && succes) @getMemory() return + if(process.env.SCREEPS_BASIC_AUTH != 0) + @signinBasicAuth() + return @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR console.log "New login request - " + new Date() options = @@ -56,6 +59,22 @@ class ScreepsStatsd token = x.token @getMemory() + ### + Sign-in using HTTP Basic Authentication (username & password). + This non-standard way of signing in is used by some private server + auth-mods. This can be disabled/enable via env-variables (see README). + ### + signinBasicAuth: () => + @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR + console.log "New login request via HTTP Basic - " + new Date() + options = + uri: process.env.SCREEPS_HOSTNAME + '/api/auth/signin' + json: true + method: 'POST' + rp(options).auth(process.env.SCREEPS_USERNAME, process.env.SCREEPS_PASSWORD, true).then (x) => + token = x.token + @getMemory() + getMemory: () => succes = false options = @@ -72,17 +91,18 @@ class ScreepsStatsd # yeah... dunno why token = x.headers['x-token'] return unless x.body.data - data = x.body.data.split('gz:')[1] - finalData = JSON.parse zlib.gunzipSync(new Buffer(data, 'base64')).toString() + data = x.body.data.substring(3) + finalData = JSON.parse zlib.inflateSync(new Buffer(data, 'base64')).toString() succes = true @report(finalData) report: (data, prefix="") => if prefix is '' console.log "Pushing to gauges - " + new Date() - if typeof v is 'object' - @report(v, prefix+k+'.') - else - @client.gauge prefix+k, v + for k,v of data + if typeof v is 'object' + @report(v, prefix+k+'.') + else + @client.gauge prefix+k, v module.exports = ScreepsStatsd From 172188f8de7a5028a31e0ca1cf2817bbc7189582 Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Apr 2017 18:33:12 -0700 Subject: [PATCH 03/10] Restored backwards compatibility -- old ansible-playbook commands now work the same as they used to, no new parameters are required for official server use. --- README.md | 24 +++++++++++++++---- docker-compose.env.example | 2 -- .../templates/docker-compose.env.j2 | 4 +++- src/ScreepsStatsd.coffee | 6 ++--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a7c4d24..9f36566 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,10 @@ You're now ready to run this whale of a command. Replace the stuff in caps with ``` ansible-playbook \ - -e screeps_hostname=https://screeps.com \ -e screeps_username=YOURUSERNAME \ -e screeps_password=YOURPASSWORD \ -e screeps_email=YOUREMAIL \ - -e screeps_baisc_auth=0 \ --user ubuntu \ - --private-key YOURPRIVATEKEY \ -i ,YOURSERVERIP \ playbook.yml ``` @@ -94,10 +91,29 @@ to apply. Refresh your browser and voila! ## Advanced configuration ### Alternative (Basic HTTP) Authentication + +To read stats from a private server, you must use the option `screeps_basic_auth=1` +and pass the additional setting `screeps_hostname` which should use the `http` protocol, +use your private server's hostname or IP, and use the screep server port (by default +`21025` unless your server changed it). + The value of `SCREEPS_BASIC_AUTH` determines how Grafana will try to login to the Screeps server. + If it's `0` the default mechanism used by the official server will be used. If it is `1` an alternative mechanism is used, using HTTP Basic Authentication. This method is required by some private server mods -such as "screepsmod-auth". +such as [screepsmod-auth](https://github.com/ScreepsMods/screepsmod-auth). + +``` +ansible-playbook \ + -e screeps_basic_auth=1 \ + -e screeps_hostname=http://your.private.server.name:21025 \ + -e screeps_username=YOURUSERNAME \ + -e screeps_password=YOURPASSWORD \ + -e screeps_email=YOUREMAIL \ + --user ubuntu \ + -i ,YOURSERVERIP \ + playbook.yml +``` ## License diff --git a/docker-compose.env.example b/docker-compose.env.example index 40fd01c..0f13245 100644 --- a/docker-compose.env.example +++ b/docker-compose.env.example @@ -1,5 +1,3 @@ -SCREEPS_HOSTNAME=https://screeps.com SCREEPS_USERNAME=bkconrad SCREEPS_EMAIL=notreally@myemail.com SCREEPS_PASSWORD=definitelynotmypassword -SCREEPS_BASIC_AUTH=0 diff --git a/roles/screeps-statsd/templates/docker-compose.env.j2 b/roles/screeps-statsd/templates/docker-compose.env.j2 index 4f1bbb0..91a0b65 100644 --- a/roles/screeps-statsd/templates/docker-compose.env.j2 +++ b/roles/screeps-statsd/templates/docker-compose.env.j2 @@ -1,3 +1,5 @@ SCREEPS_USERNAME={{ screeps_username }} SCREEPS_EMAIL={{ screeps_email }} -SCREEPS_PASSWORD={{ screeps_password }} \ No newline at end of file +SCREEPS_PASSWORD={{ screeps_password }} +SCREEPS_HOSTNAME={{ screeps_hostname|default ('https://screeps.com') }} +SCREEPS_BASIC_AUTH={{ screeps_basic_auth|default ('0') }} \ No newline at end of file diff --git a/src/ScreepsStatsd.coffee b/src/ScreepsStatsd.coffee index e7b1af9..ce9cfc8 100644 --- a/src/ScreepsStatsd.coffee +++ b/src/ScreepsStatsd.coffee @@ -43,13 +43,13 @@ class ScreepsStatsd if(token != "" && succes) @getMemory() return - if(process.env.SCREEPS_BASIC_AUTH != 0) + if(process.env.SCREEPS_BASIC_AUTH == 1) @signinBasicAuth() return @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR console.log "New login request - " + new Date() options = - uri: process.env.SCREEPS_HOSTNAME + '/api/auth/signin' + uri: (process.env.SCREEPS_HOSTNAME || 'https://screeps.com') + '/api/auth/signin' json: true method: 'POST' body: @@ -68,7 +68,7 @@ class ScreepsStatsd @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR console.log "New login request via HTTP Basic - " + new Date() options = - uri: process.env.SCREEPS_HOSTNAME + '/api/auth/signin' + uri: (process.env.SCREEPS_HOSTNAME || 'https://screeps.com') + '/api/auth/signin' json: true method: 'POST' rp(options).auth(process.env.SCREEPS_USERNAME, process.env.SCREEPS_PASSWORD, true).then (x) => From 51ed3db321b87891de5658ed7acd28bf17923b63 Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Apr 2017 18:34:49 -0700 Subject: [PATCH 04/10] Added development notes to help people who are trying to modify screeps-grafana. (Thanks @ags131) --- README.md | 28 +++++++++++++++++++ roles/screeps-statsd/tasks/main.yml | 8 +++--- .../templates/docker-compose.yml.j2 | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) rename docker-compose.yml => roles/screeps-statsd/templates/docker-compose.yml.j2 (94%) diff --git a/README.md b/README.md index 9f36566..7affab2 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,34 @@ ansible-playbook \ playbook.yml ``` + +## Important Development Notes + +If you're modifying `screeps-grafana` and wanting to test your changes, you'll want +to know the following: + +After you make changes to your own fork of this repository, you will need to generate +a docker image, publish it to Docker Hub and modify `docker-compose.yml` to use your own +custom docker image rather than `screepers/screeps-statsd`. + +Where `YOU` is your [Docker Hub](https://hub.docker.com) username: + +```bash +sudo docker build -t YOU/screeps-statsd . +sudo docker login -u YOU -p YOUR_DOCKER_HUB_PASSWORD +sudo docker push YOU/screeps-statsd +``` + +After completing these steps, your new custom image `YOU/screeps-statsd` will exist +on Docker Hub and you can use it. + +Add `-e screeps_node_image=YOU/screeps-statsd` to the `ansible-playbook` command line +to use your own Docker Hub image instead of the default. + +Each time you want to test new changes, you need to `docker build`, `docker push` +and then `ansible-playbook` to update your container with your new code. + + ## License This software is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for more information. diff --git a/roles/screeps-statsd/tasks/main.yml b/roles/screeps-statsd/tasks/main.yml index 3bb5510..40b39e3 100644 --- a/roles/screeps-statsd/tasks/main.yml +++ b/roles/screeps-statsd/tasks/main.yml @@ -58,10 +58,10 @@ - /data/data/grafana:/opt/grafana/data when: data_volume.stat.exists -- name: copy docker-compose.yml - copy: - src: docker-compose.yml - dest: /root/ +- name: Write docker-compose.yml + template: + src: docker-compose.yml.j2 + dest: /root/docker-compose.yml - name: create docker-compose.override.yml copy: diff --git a/docker-compose.yml b/roles/screeps-statsd/templates/docker-compose.yml.j2 similarity index 94% rename from docker-compose.yml rename to roles/screeps-statsd/templates/docker-compose.yml.j2 index 07bc9f0..be4669b 100644 --- a/docker-compose.yml +++ b/roles/screeps-statsd/templates/docker-compose.yml.j2 @@ -1,7 +1,7 @@ version: "2" services: node: - image: screepers/screeps-statsd + image: {{ screeps_node_image|default ('screepers/screeps-statsd') }} restart: always links: - statsd From 6f3dcb71707c69351456fe3357662d7ceeb447cb Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Apr 2017 18:35:46 -0700 Subject: [PATCH 05/10] Ignore node_modules --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0eb1a3e..e5b610c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +/node_modules/ docker-compose.env From a7bcd8910215327d05d0adfa61ab1598333efb8f Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Apr 2017 20:32:09 -0700 Subject: [PATCH 06/10] Improved debugging --- src/ScreepsStatsd.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ScreepsStatsd.coffee b/src/ScreepsStatsd.coffee index ce9cfc8..130185a 100644 --- a/src/ScreepsStatsd.coffee +++ b/src/ScreepsStatsd.coffee @@ -43,18 +43,19 @@ class ScreepsStatsd if(token != "" && succes) @getMemory() return - if(process.env.SCREEPS_BASIC_AUTH == 1) + #console.log "ENV = " + JSON.stringify(process.env) + if process.env.SCREEPS_BASIC_AUTH and process.env.SCREEPS_HOSTNAME @signinBasicAuth() return @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR - console.log "New login request - " + new Date() options = - uri: (process.env.SCREEPS_HOSTNAME || 'https://screeps.com') + '/api/auth/signin' + uri: 'https://screeps.com/api/auth/signin' json: true method: 'POST' body: email: process.env.SCREEPS_EMAIL password: process.env.SCREEPS_PASSWORD + console.log "New login request - " + options.uri + " - " + new Date() rp(options).then (x) => token = x.token @getMemory() @@ -66,11 +67,11 @@ class ScreepsStatsd ### signinBasicAuth: () => @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR - console.log "New login request via HTTP Basic - " + new Date() options = - uri: (process.env.SCREEPS_HOSTNAME || 'https://screeps.com') + '/api/auth/signin' + uri: process.env.SCREEPS_HOSTNAME + '/api/auth/signin' json: true method: 'POST' + console.log "New login request via HTTP Basic - " + options.uri + " - " + new Date() rp(options).auth(process.env.SCREEPS_USERNAME, process.env.SCREEPS_PASSWORD, true).then (x) => token = x.token @getMemory() From 7e293292845d9741b77631fad5fdd18fdafbf763 Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Apr 2017 21:05:01 -0700 Subject: [PATCH 07/10] Hotfix production server --- src/ScreepsStatsd.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ScreepsStatsd.coffee b/src/ScreepsStatsd.coffee index 130185a..f3a7c80 100644 --- a/src/ScreepsStatsd.coffee +++ b/src/ScreepsStatsd.coffee @@ -44,12 +44,12 @@ class ScreepsStatsd @getMemory() return #console.log "ENV = " + JSON.stringify(process.env) - if process.env.SCREEPS_BASIC_AUTH and process.env.SCREEPS_HOSTNAME + if process.env.SCREEPS_BASIC_AUTH == 1 @signinBasicAuth() return @client = new StatsD host: process.env.GRAPHITE_PORT_8125_UDP_ADDR options = - uri: 'https://screeps.com/api/auth/signin' + uri: process.env.SCREEPS_HOSTNAME + '/api/auth/signin' json: true method: 'POST' body: From c25ba1ff4c001473df6697b2ebecdb0f326ee272 Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Apr 2017 20:43:08 -0700 Subject: [PATCH 08/10] Add support for memory segments --- README.md | 13 +++++++ .../templates/docker-compose.env.j2 | 3 +- src/ScreepsStatsd.coffee | 37 +++++++++++++------ 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 7affab2..52f9752 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,19 @@ Create a password for your screeps account if you haven't already. You're now ready to run this whale of a command. Replace the stuff in caps with values that makes sense +For `YOURSOURCE` use either `memory` (the default) or `segment=N` where `N` is the number +of the memory segment that holds your stats. + +Examples: + +- `-e screeps_stats_source=memory` is the default and need not be specified. +It will get statistics from `Memory.stats` +- `-e screeps_stats_source=segment=0` will get statistics from the `stats` key +in the JSON data stored in segment `0` + +### Ansible Command-line + + ``` ansible-playbook \ -e screeps_username=YOURUSERNAME \ diff --git a/roles/screeps-statsd/templates/docker-compose.env.j2 b/roles/screeps-statsd/templates/docker-compose.env.j2 index 91a0b65..837dded 100644 --- a/roles/screeps-statsd/templates/docker-compose.env.j2 +++ b/roles/screeps-statsd/templates/docker-compose.env.j2 @@ -2,4 +2,5 @@ SCREEPS_USERNAME={{ screeps_username }} SCREEPS_EMAIL={{ screeps_email }} SCREEPS_PASSWORD={{ screeps_password }} SCREEPS_HOSTNAME={{ screeps_hostname|default ('https://screeps.com') }} -SCREEPS_BASIC_AUTH={{ screeps_basic_auth|default ('0') }} \ No newline at end of file +SCREEPS_BASIC_AUTH={{ screeps_basic_auth|default ('0') }} +SCREEPS_STATS_SOURCE={{ screeps_stats_source|default ('memory') }} diff --git a/src/ScreepsStatsd.coffee b/src/ScreepsStatsd.coffee index f3a7c80..62d3750 100644 --- a/src/ScreepsStatsd.coffee +++ b/src/ScreepsStatsd.coffee @@ -6,6 +6,7 @@ For full copyright and license information, please see the LICENSE file @author Bryan Conrad @copyright 2016 Bryan Conrad +@copyright 2017 Ross Perkins @link https://github.com/hopsoft/docker-graphite-statsd @license http://choosealicense.com/licenses/MIT MIT License ### @@ -78,32 +79,44 @@ class ScreepsStatsd getMemory: () => succes = false + apiEndpoint = '/api/user/memory' + fromSegment = process.env.SCREEPS_STATS_SOURCE && process.env.SCREEPS_STATS_SOURCE != 'memory' + if fromSegment + apiEndpoint = '/api/user/memory-segment?' + process.env.SCREEPS_STATS_SOURCE options = - uri: process.env.SCREEPS_HOSTNAME + '/api/user/memory' + uri: process.env.SCREEPS_HOSTNAME + apiEndpoint method: 'GET' json: true resolveWithFullResponse: true headers: "X-Token": token "X-Username": token - qs: + # segment api doesn't support limiting scope to 'stats' element + if not fromSegment + options.qs = path: 'stats' + #console.log "Using request options: " + JSON.stringify(options) rp(options).then (x) => # yeah... dunno why token = x.headers['x-token'] return unless x.body.data - data = x.body.data.substring(3) - finalData = JSON.parse zlib.inflateSync(new Buffer(data, 'base64')).toString() succes = true - @report(finalData) + if fromSegment + # segments come as plain text, not deflated + finalData = JSON.parse x.body.data + # Use only the 'stats' data from this segment, in case there is other stuff + @report(finalData.stats) + else + # memory comes deflated, first 3 chars "gz:" to indicate the deflation + data = x.body.data.substring(3) + finalData = JSON.parse zlib.inflateSync(new Buffer(data, 'base64')).toString() + @report(finalData) report: (data, prefix="") => - if prefix is '' - console.log "Pushing to gauges - " + new Date() - for k,v of data - if typeof v is 'object' - @report(v, prefix+k+'.') - else - @client.gauge prefix+k, v + for k,v of data + if typeof v is 'object' + @report(v, prefix+k+'.') + else + @client.gauge prefix+k, v module.exports = ScreepsStatsd From b0b2c8fd1677beb612b1d2aec96e265ebc439770 Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Jul 2017 09:04:43 -0700 Subject: [PATCH 09/10] Add .gitattributes to force LF line endings --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2705ec3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.coffee text eol=lf +*.j2 text eol=lf From 9a2d679966f0fa3e2e66f102638575c3db1d9f27 Mon Sep 17 00:00:00 2001 From: Ross Perkins Date: Sun, 9 Jul 2017 09:28:48 -0700 Subject: [PATCH 10/10] Added back node:image that was erroneously removed by upstream commit --- roles/screeps-statsd/templates/docker-compose.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/screeps-statsd/templates/docker-compose.yml.j2 b/roles/screeps-statsd/templates/docker-compose.yml.j2 index 60a850e..4ef280f 100644 --- a/roles/screeps-statsd/templates/docker-compose.yml.j2 +++ b/roles/screeps-statsd/templates/docker-compose.yml.j2 @@ -1,7 +1,7 @@ version: "2" services: node: -# image: {{ screeps_node_image|default ('screepers/screeps-statsd') }} + image: {{ screeps_node_image|default ('screepers/screeps-statsd') }} build: context: . dockerfile: Dockerfile