diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index a44ffea18a..5ea8d56e60 100755 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -59,6 +59,8 @@ pages: - ['userguide/dockerlinks.md', 'User Guide', 'Linking containers together' ] - ['userguide/dockervolumes.md', 'User Guide', 'Managing data in containers' ] - ['userguide/dockerrepos.md', 'User Guide', 'Working with Docker Hub' ] +- ['userguide/level1.md', '**HIDDEN**' ] +- ['userguide/level2.md', '**HIDDEN**' ] # Docker Hub docs: - ['docker-hub/index.md', 'Docker Hub', 'Docker Hub' ] diff --git a/docs/sources/userguide/dockerimages.md b/docs/sources/userguide/dockerimages.md index a4a1ac7861..a0a30408c6 100644 --- a/docs/sources/userguide/dockerimages.md +++ b/docs/sources/userguide/dockerimages.md @@ -168,6 +168,8 @@ update and create images. 1. We can update a container created from an image and commit the results to an image. 2. We can use a `Dockerfile` to specify instructions to create an image. +To learn more, check out the [Dockerfile tutorial](/userguide/level1). + ### Updating and committing an image To update an image we first need to create a container from the image @@ -478,6 +480,10 @@ We can then create a container from our new image. > To help you write a clear, readable, maintainable `Dockerfile`, we've also > written a [`Dockerfile` Best Practices guide](/articles/dockerfile_best-practices). +### More + +To learn more, check out the [Dockerfile tutorial](/userguide/level1). + ## Setting tags on an image You can also add a tag to an existing image after you commit or build it. We @@ -535,3 +541,4 @@ by linking together multiple Docker containers. Go to [Linking Containers Together](/userguide/dockerlinks). + diff --git a/docs/sources/userguide/level1.md b/docs/sources/userguide/level1.md new file mode 100644 index 0000000000..eca816250a --- /dev/null +++ b/docs/sources/userguide/level1.md @@ -0,0 +1,72 @@ +page_title: Docker Images Test +page_description: How to work with Docker images. +page_keywords: documentation, docs, the docker guide, docker guide, docker, docker platform, virtualization framework, docker.io, Docker images, Docker image, image management, Docker repos, Docker repositories, docker, docker tag, docker tags, Docker Hub, collaboration + +Back + +# Dockerfile Tutorial + +## Test your Dockerfile knowledge - Level 1 + +### Questions + +
+
+ + + + ++# This is a Dockerfile to create an image with Memcached and Emacs installed.+
+# VERSION 1.0
+# use the ubuntu base image provided by dotCloud + ub
+ E B, eric.bardin@dotcloud.com
+# make sure the package repository is up to date + echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list + apt-get update
+# install memcached +RUN apt-get install -y
+# install emacs + apt-get install -y emacs23 +
In the next level, we will go into more detail about how to specify which command should be executed when the container starts, +which user to use, and how expose a particular port.
+ +Back +Go to the next level \ No newline at end of file diff --git a/docs/sources/userguide/level2.md b/docs/sources/userguide/level2.md new file mode 100644 index 0000000000..c4f2a2802c --- /dev/null +++ b/docs/sources/userguide/level2.md @@ -0,0 +1,97 @@ +page_title: Docker Images Test +page_description: How to work with Docker images. +page_keywords: documentation, docs, the docker guide, docker guide, docker, docker platform, virtualization framework, docker.io, Docker images, Docker image, image management, Docker repos, Docker repositories, docker, docker tag, docker tags, Docker Hub, collaboration + +Back + +#Dockerfile Tutorial + +## Test your Dockerfile knowledge - Level 2 + +### Questions: + ++ +
+ + + + ++# Redis +# +# VERSION 0.42 +# +# use the ubuntu base image provided by dotCloud + ub+ + +
+MAINT Ro Ha roberto.hashioka@dotcloud.com
+# make sure the package repository is up to date + echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list + apt-get update
+# install wget (required for redis installation) + apt-get install -y wget
+# install make (required for redis installation) + apt-get install -y make
+# install gcc (required for redis installation) +RUN apt-get install -y
+# install apache2 + wget http://download.redis.io/redis-stable.tar.gz +tar xvzf redis-stable.tar.gz +cd redis-stable && make && make install
+# launch redis when starting the image + ["redis-server"]
+# run as user dameon + daemon
+# expose port 6379 + 6379 +
+Thanks for going through our tutorial! We will be posting Level 3 shortly. Follow us on twitter
+Follow @docker
+
+
In the meantime, check out this blog post by Michael Crosby that describes Dockerfile Best Practices.
+Back to the Docs! \ No newline at end of file diff --git a/docs/sources/userguide/usingdocker.md b/docs/sources/userguide/usingdocker.md index 928db5148e..e64db0bc2e 100644 --- a/docs/sources/userguide/usingdocker.md +++ b/docs/sources/userguide/usingdocker.md @@ -114,8 +114,7 @@ We've specified an image: `training/webapp`. This image is a pre-built image we've created that contains a simple Python Flask web application. -Lastly, we've specified a command for our container to run: `python app.py`. -This launches our web application. +Lastly, we've specified a command for our container to run: `python app.py`. This launches our web application. > **Note:** > You can see more detail on the `docker run` command in the [command diff --git a/docs/theme/mkdocs/base.html b/docs/theme/mkdocs/base.html index 09de2b3511..a4a10a927d 100644 --- a/docs/theme/mkdocs/base.html +++ b/docs/theme/mkdocs/base.html @@ -17,6 +17,7 @@ + @@ -102,9 +103,12 @@ {% include "footer.html" %} + + + diff --git a/docs/theme/mkdocs/css/dockerfile_tutorial.css b/docs/theme/mkdocs/css/dockerfile_tutorial.css new file mode 100644 index 0000000000..79d0e9cfdf --- /dev/null +++ b/docs/theme/mkdocs/css/dockerfile_tutorial.css @@ -0,0 +1,59 @@ +pre { + background-color: #F5F5F5; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px 4px 4px 4px; + display: block; + font-size: 13px; + line-height: 20px; + margin: 0 0 10px; + padding: 9.5px; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; +} +code, pre { + border-radius: 3px 3px 3px 3px; + color: #333333; + font-family: Monaco,Menlo,Consolas,"Courier New",monospace; + font-size: 12px; + padding: 0 3px 2px; +} + +.terminal { + color: #AAAAAA; + background-color: black; +} + +.terminal span.command { + color: white; + font-weight: bold; +} + +.error_input { + border-color: #B94A48 !important; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset !important; +} + +input.level { + width: 150px; + height: 2.5em; + display: inline-block; +} +div.level_error { + width: 300px; + display: inline-block; + padding: 7px 12px; +} +.alert { + text-align:center; +} +#check_level_questions { + margin-top: 10px; +} +.form-inline input.l_fill { + height: 20px; + border-radius: 0; + width: 90px; + margin-right: 0; + padding: 0 0 2px 0; +} \ No newline at end of file diff --git a/docs/theme/mkdocs/js/dockerfile_tutorial.js b/docs/theme/mkdocs/js/dockerfile_tutorial.js new file mode 100755 index 0000000000..a17cc48d61 --- /dev/null +++ b/docs/theme/mkdocs/js/dockerfile_tutorial.js @@ -0,0 +1,74 @@ +function clean_input(i) { + return i.replace(/^\s+|\s+$/g, ''); +} + +function clean_up(str){ + return clean_input(str).toUpperCase(); +} + +function dockerfile_log(level, item, errors) +{ + var logUrl = '/tutorial/api/dockerfile_event/'; + $.ajax({ + url: logUrl, + type: "POST", + cache:false, + data: { + 'errors': errors, + 'level': level, + 'item': item, + }, + }).done( function() { } ); +} + +function validate_email(email) +{ + var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(email); +} + +$(document).ready(function() { + + /* prepare to send the csrf-token on each ajax-request */ + var csrftoken = $.cookie('csrftoken'); + $.ajaxSetup({ + headers: { 'X-CSRFToken': csrftoken } + }); + + $("#send_email").click( function() + { + $('#email_invalid').hide(); + $('#email_already_registered').hide(); + $('#email_registered').hide(); + + email = $('#email').val(); + if (!validate_email(email)) + { + $('#email_invalid').show(); + return (false); + } + + var emailUrl = '/tutorial/api/subscribe/'; + + $.ajax({ + url: emailUrl, + type: "POST", + cache:false, + data: { + 'email': email, + 'from_level': $(this).data('level') + }, + }).done( function(data ) { + if (data == 1) // already registered + { + $('#email_already_registered').show(); + } + else if (data == 0) // registered ok + { + $('#email_registered').show(); + } + + } ); + return (true); + }); +}) diff --git a/docs/theme/mkdocs/js/dockerfile_tutorial_level.js b/docs/theme/mkdocs/js/dockerfile_tutorial_level.js new file mode 100644 index 0000000000..22f13b6ef7 --- /dev/null +++ b/docs/theme/mkdocs/js/dockerfile_tutorial_level.js @@ -0,0 +1,176 @@ +function check_form1 () +{ + $('#level1_error0').hide(); + $('#level1_error1').hide(); + $('#level1_error2').hide(); + $('#level1_error3').hide(); + + $('#no_good').hide(); + $('#some_good').hide(); + $('#all_good').hide(); + + var a = clean_input($('#level1_q0').val()).toUpperCase(); + var b = clean_input($('#level1_q1').val()).toUpperCase(); + var c = clean_input($('#level1_q2').val()).toUpperCase(); + var d = clean_input($('#level1_q3').val()); + var points = 0; + + if (a == 'FROM'){ + points = points + 1; + } else { + $('#level1_error0').show(); + } + if (b == 'RUN') { + points = points + 1; + } else { + $('#level1_error1').show(); + } + if (c == 'MAINTAINER') { + points = points + 1; + } else { + $('#level1_error2').show(); + } + if (d == '#') { + points = points + 1; + } else { + $('#level1_error3').show(); + } + if (points == 4) {// all good + $('#all_good').show(); + } else if (points == 0) { // nothing good + $('#no_good').show(); + } else {// some good some bad + $('#some_good').show(); + } + return (4 - points); +} + +function check_form2 () +{ + $('.level_questions .alert').hide(); + + var answers = {}; + answers[0] = ['FROM']; + answers[1] = ['ENTRYPOINT', 'CMD']; + answers[2] = ['#']; + answers[3] = ['USER']; + answers[4] = ['RUN']; + answers[5] = ['EXPOSE']; + answers[6] = ['MAINTAINER']; + answers[7] = ['ENTRYPOINT', 'CMD']; + + var points = 0; + + $.each($(".level"), function(num, input){ + var cleaned = clean_up(input.value); + if ($.inArray(cleaned, answers[num]) == -1) { + $( $(".level_error")[num]).show() + $(input).addClass("error_input"); + } else { + $( $(".level_error")[num]).hide() + $(input).removeClass("error_input"); + points += 1; + } + }) + if (points == 8) // all good + { + $('#all_good').show(); + } + else if (points == 0) // nothing good + { + $('#no_good').show(); + } + else // some good some bad + { + $('#some_good').show(); + } + return (8- points); +} + +function check_fill(answers) +{ + $('#dockerfile_ok').hide(); + $('#dockerfile_ko').hide(); + + var errors = 0; + + $.each($(".l_fill"), function(num, input){ + var cleaned = clean_up(input.value); + var id = input.id; + if (answers[id] != cleaned) { + $(input).addClass("error_input"); + errors += 1; + } else { + $(input).removeClass("error_input"); + } + }); + + if (errors != 0) + { + $('#dockerfile_ko').show(); + } + else + { + $('#dockerfile_ok').show(); + } + return (errors); +} + +$(document).ready(function() { + + $("#check_level1_questions").click( function(){ + errors = check_form1(); + dockerfile_log(1, '1_questions', errors); + } + ); + + $("#check_level1_fill").click( function(){ + var answers = {}; + answers['from'] = 'FROM'; + answers['ubuntu'] = 'UNTU'; + answers['maintainer'] = 'MAINTAINER'; + answers['eric'] = 'RIC'; + answers['bardin'] = 'ARDIN'; + answers['run0'] = 'RUN'; + answers['run1'] = 'RUN'; + answers['run2'] = 'RUN'; + answers['memcached'] = 'MEMCACHED'; + + var errors = check_fill(answers); + dockerfile_log(1, '2_fill', errors); + }); + + $("#check_level2_questions").click( function(){ + errors = check_form2(); + dockerfile_log(2, '1_questions', errors); + } + ); + + $("#check_level2_fill").click( function(){ + var answers = {}; + answers['from'] = "FROM"; + answers['ubuntu'] = "UNTU"; + answers['maintainer'] = "AINER"; + answers['roberto'] = "BERTO"; + answers['hashioka'] = "SHIOKA"; + answers['run0'] = "RUN"; + answers['run1'] = "RUN"; + answers['run2'] = "RUN"; + answers['run3'] = "RUN"; + answers['run4'] = "RUN"; + answers['run5'] = "RUN"; + answers['run6'] = "RUN"; + answers['entrypoint'] = "ENTRYPOINT"; + answers['user'] = "USER"; + answers['expose'] = "EXPOSE"; + answers['gcc'] = "GCC"; + + var errors = check_fill(answers); + dockerfile_log(2, '2_fill', errors); + }); + + $(".btn.btn-primary.back").click( function(event){ + event.preventDefault(); + window.history.back(); + }) +}); diff --git a/docs/theme/mkdocs/js/jquery.cookie.js b/docs/theme/mkdocs/js/jquery.cookie.js new file mode 100644 index 0000000000..3fb201c6a0 --- /dev/null +++ b/docs/theme/mkdocs/js/jquery.cookie.js @@ -0,0 +1,90 @@ +/*! + * jQuery Cookie Plugin v1.3.1 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2013 Klaus Hartl + * Released under the MIT license + */ +(function ($, document, undefined) { + + var pluses = /\+/g; + + function raw(s) { + return s; + } + + function decoded(s) { + return unRfc2068(decodeURIComponent(s.replace(pluses, ' '))); + } + + function unRfc2068(value) { + if (value.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape + value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + return value; + } + + function fromJSON(value) { + return config.json ? JSON.parse(value) : value; + } + + var config = $.cookie = function (key, value, options) { + + // write + if (value !== undefined) { + options = $.extend({}, config.defaults, options); + + if (value === null) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = config.json ? JSON.stringify(value) : String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // read + var decode = config.raw ? raw : decoded; + var cookies = document.cookie.split('; '); + var result = key ? null : {}; + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = decode(parts.join('=')); + + if (key && key === name) { + result = fromJSON(cookie); + break; + } + + if (!key) { + result[name] = fromJSON(cookie); + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) !== null) { + $.cookie(key, null, options); + return true; + } + return false; + }; + +})(jQuery, document);