AF-1709: Documenting new system property (#1341)

[[_wb.installation]]
= Installation

[[_wb.warinstallation]]
== War installation

Use the `war` from the workbench distribution zip that corresponds to your application server.
The differences between these `war` files are mainly superficial.
For example, some JARs might be excluded if the application server already supplies them.

* ``eap7``: tailored for Red Hat JBoss Enterprise Application Platform 7
* ``wildfly14``: tailored for Wildfly 14

[[_wb.workbenchdata]]
== {CENTRAL} data

The workbench stores its data, by default in the directory ``$WORKING_DIRECTORY/.niogit``, for example ``wildfly-14.0.1.Final/bin/.niogit``, but it can be overridden with the <<_wb.systemproperties,system property>>``-Dorg.uberfire.nio.git.dir``.

[NOTE]
====
In production, make sure to back up the workbench data directory.
====

[[_wb.systemproperties]]
== System properties

Here's a list of all system properties:

* **``org.kie.workbench.profile``**: Selects the workbench profile. Possible values are `FULL` or `PLANNER_AND_RULES`. A prefix `FULL_` will set the profile and hide the profile preferences from the admin preferences. Default: `FULL`.
* **``kie.maven.offline.force``**: Forces Maven to behave as offline. If true, disable online dependency resolution. Default: false.
* **``org.appformer.m2repo.url``**: Location of the for the default Maven repository the workbench uses when looking for dependencies. Usually this points to the Maven repository inside the Workbench for example ``http://localhost:8080/kie-wb/maven2``. Please set this before starting up the Workbench. Default: File path to the inner m2 repository.
* **``org.uberfire.nio.git.dir``**: Location of the directory ``$$.$$niogit``. Default: working directory
* **``org.uberfire.nio.git.dirname``**: Name of the git directory. Default: `.niogit`
* **``org.uberfire.nio.git.proxy.ssh.over.http``**: Defines that SSH should use an HTTP Proxy. Default: `false`
* **``http.proxyHost``**: Defines the host name of the HTTP Proxy. Default: `null`
* **``http.proxyPort``**: Defines the host port (integer value) of the HTTP Proxy. Default: `null`
* **``org.uberfire.nio.git.proxy.ssh.over.https``**: Defines that SSH should use an HTTPS Proxy. Default: `false`
* **``https.proxyHost``**: Defines the host name of the HTTPS Proxy. Default: `null`
* **``https.proxyPort``**: Defines the host port (integer value) of the HTTPS Proxy. Default: `null`
* **``org.uberfire.nio.git.daemon.enabled``**: Enables/disables git daemon. Default: `true`
* **``org.uberfire.nio.git.daemon.host``**: If git daemon enabled, uses this property as local host identifier. Default: `localhost`
* **``org.uberfire.nio.git.daemon.port``**: If git daemon enabled, uses this property as port number. Default: `9418`
+

[NOTE]
====
If the default or assigned port is already in use, a new port is automatically selected. Ensure that the ports are available and check the log for more information.
====
* **``org.uberfire.nio.git.ssh.enabled``**: Enables/disables ssh daemon. Default: `true`
* **``org.uberfire.nio.git.ssh.host``**: If ssh daemon enabled, uses this property as local host identifier. Default: `localhost`
* **``org.uberfire.nio.git.ssh.port``**: If ssh daemon enabled, uses this property as port number. Default: `8001`
+

[NOTE]
====
If the default or assigned port is already in use, a new port is automatically selected. Ensure that the ports are available and check the log for more information.
====
* **``org.uberfire.nio.git.ssh.cert.dir``**: Location of the directory `$$.$$security` where local certificates will be stored. Default: working directory
* **``org.uberfire.nio.git.ssh.passphrase``**: Passphrase to access your Operating Systems public keystore when cloning `git` repositories with `scp` style URLs; e.g. ``git@github.com:user/repository.git``.
* **``org.uberfire.nio.git.ssh.algorithm``**: Algorithm used by SSH. Default: `DSA`
+

[NOTE]
====
If you plan to use RSA or any algorithm other than DSA, make sure you setup properly your Application Server to use Bouncy Castle JCE library.
====
* **``appformer.ssh.keystore``**: Defines the custom SSH keystore to be used with Workbench by specifying a class name. If the property is not available the default SSH keystore is used.
* **``appformer.ssh.keys.storage.folder``**: When using the default SSH keystore, this parameter defines the storage folder for the user's SSH public keys. If the property is not available the keys are stored in the Workbench ``.security`` folder.
* **``org.uberfire.metadata.index.dir``**: Place where Lucene `$$.$$index` folder will be stored. Default: working directory
* **``org.uberfire.ldap.regex.role_mapper``**: Regex pattern used to map LDAP principal names to application role name.  Note that the variable `role` must be part of the pattern as it is substited by the application role name when matching a principal value to role name. Default: Not used.
* **``org.uberfire.sys.repo.monitor.disabled``**: Disable configuration monitor (do not disable unless you know what you're doing). Default: `false`
* **``org.uberfire.secure.key``**: Secret password used by password encryption. Default: `org.uberfire.admin`
* **``org.uberfire.secure.alg``**: Crypto algorithm used by password encryption. Default: `PBEWithMD5AndDES`
* **``org.uberfire.domain``**: security-domain name used by uberfire. Default: `ApplicationRealm`
* **``appformer.experimental.features``**: enables the _Experimental Features Framework_
* **``org.guvnor.m2repo.dir``**: Place where Maven repository folder will be stored. Default: working-directory/repositories/kie
* **``org.guvnor.project.gav.check.disabled``**: Disable GAV checks. Default: `false`
* **``org.kie.demo``**: Enables external clone of a demo application from GitHub.
* **``org.kie.build.disable-project-explorer``**: Disable automatic build of selected Project in Project Explorer. Default: `false`
* **``org.kie.verification.disable-dtable-realtime-verification``**: Disables the realtime validation and verification of decision tables. Default: `false`
* **``org.kie.workbench.controller``**: URL for connecting with a {CONTROLLER}, for example: `ws://localhost:8080/kie-server-controller/websocket/controller`.
[NOTE]
====
Only Web Socket protocol is supported for connecting with a {HEADLESS_CONTROLLER}.
When specifying this proporty, the Workbench will automatically disable all the features related to running the embedded {CONTROLLER}.
====
* **``org.kie.workbench.controller.user``**: User name for connecting with a {CONTROLLER}. Default: `kieserver`
* **``org.kie.workbench.controller.pwd``**: Password for connecting with a {CONTROLLER}. Default: `kieserver1!`
* **``org.kie.workbench.controller.token``**: Token string for connecting with a {CONTROLLER}.
[NOTE]
====
Please refer to <<usingTokenBasedAuthentication, Using token based authentication>> for more details about how to use token based authentication.
====
* **``kie.keystore.keyStoreURL``**: URL to a keystore which should be used for connecting with a {HEADLESS_CONTROLLER}.
* **``kie.keystore.keyStorePwd``**: Password to a keystore.
* **``kie.keystore.key.ctrl.alias``**: Alias of the key where password is stored.
* **``kie.keystore.key.ctrl.pwd``**: Password of an alias with stored password.
[NOTE]
====
Please refer to <<_securing_password_using_key_store, Securing password using key store>> for more details about how to use a key store for securing your passwords.
====

To change one of these system properties in a WildFly or JBoss EAP cluster:

. Edit the file ``$JBOSS_HOME/domain/configuration/host.xml``.
. Locate the XML elements `server` that belong to the `main-server-group` and add a system property, for example:
+

[source,xml]
----
<system-properties>
  <property name="org.uberfire.nio.git.dir" value="..." boot-time="false"/>
  ...
</system-properties>
----

[[_wb.troubleshooting]]
== Trouble shooting

[[_wb.troubleshootingloadingspinner]]
=== Loading.. does not disappear and Workbench fails to show

There have been reports that Firewalls in between the server and the browser can interfere with Server Sent Events (SSE) used by the Workbench.

The issue results in the "Loading..." spinner remaining visible and the Workbench failing to materialize.

The workaround is to disable the Workbench's use of Server Sent Events by adding file `/WEB-INF/classes/ErraiService.properties` to the exploded WAR containing the value ``errai.bus.enable_sse_support=false``.
Re-package the WAR and re-deploy.

Some Users have also reported disabling Server Sent Events does not resolve the issue. The solution found to work is to configure the JVM to use a different Entropy Gathering Device on Linux for `SecureRandom`. This can be configured by setting System Property `java.security.egd` to `file:/dev/./urandom`. See http://stackoverflow.com/questions/33166198/kie-workbench-not-loading-after-login/39110177#39110177[this]  Stack Overflow post for details.

Please note however this affects the JVM's random  number generation and may present other challenges where strong cryptography is required. Configure with caution.

=== Not able to clone KIE Workbench Git repository using ssh protocol.
Git clients using ssh to interact with the Git server that is bundled with Workbench are authenticated and authorized to perform git commands by the security API that is part of the Uberfire backend server.  When using an LDAP security realm, some git clients were not being authorized as expected.  This was due to the fact that for non-web clients such as Git via ssh, the principal (i.e., user or group) name assigned to a user by the application server's user registry is the more complex DN associated to that principal by LDAP. The logic of the Uberfire backend server looked for on exact match of roles allowed with the principal name returned and therefore failed.

It is now possible to control the role-principal matching via the system property

[source, property]
----
org.uberfire.ldap.regex.role_mapper
----

which takes as its value a Regex pattern to be applied when matching LDAP principal to role names.  The pattern must contain the literal word variable 'role'.  During authorization the variable is replaced by each of the allow application roles.  If the pattern is matched the role is added to the user.

For instance, if the DN for the admin group in LDAP is

[source, property]
----
DN: cn=admin,ou=groups,dc=example,dc=com
----

and its intended role is admin, then setting `org.uberfire.ldap.regex.role_mapper` with value

[source, regex]
----
cn[\\ ]*=[\\ ]*role
----

will find a match on role 'admin'.

[[_wb.installation]]
= Installation

[[_wb.warinstallation]]
== War installation

Use the `war` from the workbench distribution zip that corresponds to your application server.
The differences between these `war` files are mainly superficial.
For example, some JARs might be excluded if the application server already supplies them.

* ``eap7``: tailored for Red Hat JBoss Enterprise Application Platform 7
* ``wildfly14``: tailored for Wildfly 14

[[_wb.workbenchdata]]
== {CENTRAL} data

The workbench stores its data, by default in the directory ``$WORKING_DIRECTORY/.niogit``, for example ``wildfly-14.0.1.Final/bin/.niogit``, but it can be overridden with the <<_wb.systemproperties,system property>>``-Dorg.uberfire.nio.git.dir``.

[NOTE]
====
In production, make sure to back up the workbench data directory.
====

[[_wb.systemproperties]]
== System properties

Here's a list of all system properties:

* **``kie.maven.offline.force``**: Forces Maven to behave as offline. If true, disable online dependency resolution. Default: false.
* **``org.appformer.m2repo.url``**: Location of the for the default Maven repository the workbench uses when looking for dependencies. Usually this points to the Maven repository inside the Workbench for example ``http://localhost:8080/kie-wb/maven2``. Please set this before starting up the Workbench. Default: File path to the inner m2 repository.
* **``org.uberfire.nio.git.dir``**: Location of the directory ``$$.$$niogit``. Default: working directory
* **``org.uberfire.nio.git.dirname``**: Name of the git directory. Default: `.niogit`
* **``org.uberfire.nio.git.proxy.ssh.over.http``**: Defines that SSH should use an HTTP Proxy. Default: `false`
* **``http.proxyHost``**: Defines the host name of the HTTP Proxy. Default: `null`
* **``http.proxyPort``**: Defines the host port (integer value) of the HTTP Proxy. Default: `null`
* **``org.uberfire.nio.git.proxy.ssh.over.https``**: Defines that SSH should use an HTTPS Proxy. Default: `false`
* **``https.proxyHost``**: Defines the host name of the HTTPS Proxy. Default: `null`
* **``https.proxyPort``**: Defines the host port (integer value) of the HTTPS Proxy. Default: `null`
* **``org.uberfire.nio.git.daemon.enabled``**: Enables/disables git daemon. Default: `true`
* **``org.uberfire.nio.git.daemon.host``**: If git daemon enabled, uses this property as local host identifier. Default: `localhost`
* **``org.uberfire.nio.git.daemon.port``**: If git daemon enabled, uses this property as port number. Default: `9418`
+

[NOTE]
====
If the default or assigned port is already in use, a new port is automatically selected. Ensure that the ports are available and check the log for more information.
====
* **``org.uberfire.nio.git.ssh.enabled``**: Enables/disables ssh daemon. Default: `true`
* **``org.uberfire.nio.git.ssh.host``**: If ssh daemon enabled, uses this property as local host identifier. Default: `localhost`
* **``org.uberfire.nio.git.ssh.port``**: If ssh daemon enabled, uses this property as port number. Default: `8001`
+

[NOTE]
====
If the default or assigned port is already in use, a new port is automatically selected. Ensure that the ports are available and check the log for more information.
====
* **``org.uberfire.nio.git.ssh.cert.dir``**: Location of the directory `$$.$$security` where local certificates will be stored. Default: working directory
* **``org.uberfire.nio.git.ssh.passphrase``**: Passphrase to access your Operating Systems public keystore when cloning `git` repositories with `scp` style URLs; e.g. ``git@github.com:user/repository.git``.
* **``org.uberfire.nio.git.ssh.algorithm``**: Algorithm used by SSH. Default: `DSA`
+

[NOTE]
====
If you plan to use RSA or any algorithm other than DSA, make sure you setup properly your Application Server to use Bouncy Castle JCE library.
====
* **``appformer.ssh.keystore``**: Defines the custom SSH keystore to be used with Workbench by specifying a class name. If the property is not available the default SSH keystore is used.
* **``appformer.ssh.keys.storage.folder``**: When using the default SSH keystore, this parameter defines the storage folder for the user's SSH public keys. If the property is not available the keys are stored in the Workbench ``.security`` folder.
* **``org.uberfire.metadata.index.dir``**: Place where Lucene `$$.$$index` folder will be stored. Default: working directory
* **``org.uberfire.ldap.regex.role_mapper``**: Regex pattern used to map LDAP principal names to application role name.  Note that the variable `role` must be part of the pattern as it is substited by the application role name when matching a principal value to role name. Default: Not used.
* **``org.uberfire.sys.repo.monitor.disabled``**: Disable configuration monitor (do not disable unless you know what you're doing). Default: `false`
* **``org.uberfire.secure.key``**: Secret password used by password encryption. Default: `org.uberfire.admin`
* **``org.uberfire.secure.alg``**: Crypto algorithm used by password encryption. Default: `PBEWithMD5AndDES`
* **``org.uberfire.domain``**: security-domain name used by uberfire. Default: `ApplicationRealm`
* **``appformer.experimental.features``**: enables the _Experimental Features Framework_
* **``org.guvnor.m2repo.dir``**: Place where Maven repository folder will be stored. Default: working-directory/repositories/kie
* **``org.guvnor.project.gav.check.disabled``**: Disable GAV checks. Default: `false`
* **``org.kie.demo``**: Enables external clone of a demo application from GitHub.
* **``org.kie.build.disable-project-explorer``**: Disable automatic build of selected Project in Project Explorer. Default: `false`
* **``org.kie.verification.disable-dtable-realtime-verification``**: Disables the realtime validation and verification of decision tables. Default: `false`
* **``org.kie.workbench.controller``**: URL for connecting with a {CONTROLLER}, for example: `ws://localhost:8080/kie-server-controller/websocket/controller`.
[NOTE]
====
Only Web Socket protocol is supported for connecting with a {HEADLESS_CONTROLLER}.
When specifying this proporty, the Workbench will automatically disable all the features related to running the embedded {CONTROLLER}.
====
* **``org.kie.workbench.controller.user``**: User name for connecting with a {CONTROLLER}. Default: `kieserver`
* **``org.kie.workbench.controller.pwd``**: Password for connecting with a {CONTROLLER}. Default: `kieserver1!`
* **``org.kie.workbench.controller.token``**: Token string for connecting with a {CONTROLLER}.
[NOTE]
====
Please refer to <<usingTokenBasedAuthentication, Using token based authentication>> for more details about how to use token based authentication.
====
* **``kie.keystore.keyStoreURL``**: URL to a keystore which should be used for connecting with a {HEADLESS_CONTROLLER}.
* **``kie.keystore.keyStorePwd``**: Password to a keystore.
* **``kie.keystore.key.ctrl.alias``**: Alias of the key where password is stored.
* **``kie.keystore.key.ctrl.pwd``**: Password of an alias with stored password.
[NOTE]
====
Please refer to <<_securing_password_using_key_store, Securing password using key store>> for more details about how to use a key store for securing your passwords.
====

To change one of these system properties in a WildFly or JBoss EAP cluster:

. Edit the file ``$JBOSS_HOME/domain/configuration/host.xml``.
. Locate the XML elements `server` that belong to the `main-server-group` and add a system property, for example:
+

[source,xml]
----
<system-properties>
  <property name="org.uberfire.nio.git.dir" value="..." boot-time="false"/>
  ...
</system-properties>
----

[[_wb.troubleshooting]]
== Trouble shooting

[[_wb.troubleshootingloadingspinner]]
=== Loading.. does not disappear and Workbench fails to show

There have been reports that Firewalls in between the server and the browser can interfere with Server Sent Events (SSE) used by the Workbench.

The issue results in the "Loading..." spinner remaining visible and the Workbench failing to materialize.

The workaround is to disable the Workbench's use of Server Sent Events by adding file `/WEB-INF/classes/ErraiService.properties` to the exploded WAR containing the value ``errai.bus.enable_sse_support=false``.
Re-package the WAR and re-deploy.

Some Users have also reported disabling Server Sent Events does not resolve the issue. The solution found to work is to configure the JVM to use a different Entropy Gathering Device on Linux for `SecureRandom`. This can be configured by setting System Property `java.security.egd` to `file:/dev/./urandom`. See http://stackoverflow.com/questions/33166198/kie-workbench-not-loading-after-login/39110177#39110177[this]  Stack Overflow post for details.

Please note however this affects the JVM's random  number generation and may present other challenges where strong cryptography is required. Configure with caution.

=== Not able to clone KIE Workbench Git repository using ssh protocol.
Git clients using ssh to interact with the Git server that is bundled with Workbench are authenticated and authorized to perform git commands by the security API that is part of the Uberfire backend server.  When using an LDAP security realm, some git clients were not being authorized as expected.  This was due to the fact that for non-web clients such as Git via ssh, the principal (i.e., user or group) name assigned to a user by the application server's user registry is the more complex DN associated to that principal by LDAP. The logic of the Uberfire backend server looked for on exact match of roles allowed with the principal name returned and therefore failed.

It is now possible to control the role-principal matching via the system property

[source, property]
----
org.uberfire.ldap.regex.role_mapper
----

which takes as its value a Regex pattern to be applied when matching LDAP principal to role names.  The pattern must contain the literal word variable 'role'.  During authorization the variable is replaced by each of the allow application roles.  If the pattern is matched the role is added to the user.

For instance, if the DN for the admin group in LDAP is

[source, property]
----
DN: cn=admin,ou=groups,dc=example,dc=com
----

and its intended role is admin, then setting `org.uberfire.ldap.regex.role_mapper` with value

[source, regex]
----
cn[\\ ]*=[\\ ]*role
----

will find a match on role 'admin'.


Update 2015-02-13-the-last-day.adoc

Update 2015-02-13-the-last-day.adoc

= 最后一天
:hp-alt-title: the last day
:published_at: 2015-02-13
:hp-tags: last, next, 最后,
:hp-image: https://raw.githubusercontent.com/senola/pictures/master/background/background1.jpg

最后一天搬砖，搬完就可以回家过年了！！！！！

这大半个月可把我给累坏了，吃不着睡不香。

出门在外特别想念着家，尤其是临近过年。

想看看家乡，看看相亲们朴实的微笑。

遗憾的是今年没有年终奖  ╮(╯▽╰)╭

不过，还是很喜欢这份工作，喜欢这里的人和事。

新的一年，新的挑战！年后再战，加油！！！

= 最后一天
:hp-alt-title: the last day
:published_at: 2015-02-13
:hp-tags: last, next, 最后
:hp-image: https://raw.githubusercontent.com/senola/pictures/master/background/background1.jpg

最后一天搬砖，搬完就可以回家过年了！！！！！

这大半个月可把我给累坏了，吃不着睡不香。

出门在外特别想念着家，尤其是临近过年。

想看看家乡，看看相亲们朴实的微笑。

遗憾的是今年没有年终奖  ╮(╯▽╰)╭

不过，还是很喜欢这份工作，喜欢这里的人和事。

新的一年，新的挑战！年后再战，加油！！！


Update 2015-11-11-Hoka-One-One.adoc

Update 2015-11-11-Hoka-One-One.adoc

= Hoka One One
:hp-image: https://cloud.githubusercontent.com/assets/19504323/15452702/48719838-202c-11e6-9c52-3d13d17bc909.jpg
:published_at: 2015-11-11
:hp-tags: Hoka, Hoka One One, Maximalist, 跑步, 跑鞋, 运动鞋, 缓震, 保护, 海淘, 亚马逊

市面上的运动鞋可以说五花八门，但选择越多，越难选择。这里我为大家推荐一个牌子，叫Hoka One One。

这是一个极小众的牌子，但它的鞋子绝对是我穿过最舒服的，可以说秒杀Asics, NB, Mizuno, Saucony等一众顶级运动鞋品牌。

是什么让Hoka如此强大？

越野跑、铁人三项、马拉松，以及更虐更变态的耐力跑比赛中，都不断出现穿着HokaOneOne的选手。越来越多登上领奖台的选手选择HokaOneOne，一个只有旗舰级产品的品牌，一个每款产品都能独当一面的品牌，一个被誉为跑界路虎的品牌。...

在赤足跑流行的两年前，五指、裸足、0落差、薄底的跑鞋可火啦，崇尚自然的，利用肢体力量带来的缓冲作用进行运动，当时的阿迪、耐克、纽巴伦都被这种趋势牵着鼻子做跑鞋。

奇怪跑鞋火了

不过有这么一家名字奇怪，叫 Hoka One One 的小厂商，一反常态的跟流行趋势对着干。在裸足奔跑盛行的年代，开始耕耘厚底强缓震跑鞋。

据说其创始人跑到中国找化学家专门研发了一种轻质又缓震的泡沫材质。利用这种材质作为中底，生产出又大又缓震的超级越野跑鞋，简直是跑鞋界的路虎。

与其它跑鞋比一下，你就知道它有多大多厚了。

Hoka One One 是毛利语，相当于“To fly”的意思，翻译成中文意思是“别拦着我，我要上天”。

多数慢跑鞋的顶级跟次顶级的差别仅仅是在顶级跑鞋加入更多缓震技术，来适应大体重的用户，典型例子就是亚瑟士的顶级 Kayano 与次顶级 GT 系列。

Asics GT-2000

2013年，誓把缓震做到极致的 Hoka One One 被 Deckers 公司相中并且收购，这家公司同样是 UGG 的母公司。有没有很期待之后发生的事。

据说UGG鞋底用了Hoka技术

那时候，Hoka One One 只有90个店面，被收购之后这怪物跑鞋就迅速流行起来，两年之内就开了1200个店面。

软到像踩着“大白”跑步
Hoka One One 一门心思做厚厚的跑步鞋，这种鞋穿起来像踩在松糕上的感觉，特别适合上百甚至几百公里的超级马拉松等长距离运动。

对于这些距离长到快要逼近你极限的奔跑，穿 Hoka One One 跑鞋能带给你无敌的舒适感。

除了针对马拉松赛事这种公路跑产品之外，Hoka OneOne 还在研发越野跑系列，带你尽情翻山越岭。

其实，最初的产品就是为了应对下山这种巨伤膝盖的运动。

轻轻的 Hoka One One 可以背在包里，下山时换上，就不必担心你的膝盖受损了。

想象一下踩着大白的感觉。

这样优秀的跑鞋不出所料的迅速占领了各大跑鞋排行榜单。在最著名的《跑者世界》推荐榜单里经常出现它的身影。

比如 Hoka One One Tracer 是本年度春季跑鞋推荐中最轻量的跑鞋之一。

Hoka One One Tracer

《跑者世界》每年都会召集全美的成百上千名测试者对各种跑鞋进行测试并给出公正客观的评分，结合评分和实际脚感给出推荐。

我在看到《跑者世界》对 Hoka One One Conquest 公路跑鞋的测试分数时，全身打了个寒颤，我和我的小伙伴都惊呆了！

前掌缓震、后跟缓震、刚度评分几乎都要满分爆表，这是在造鞋么，简直是在造筋斗云嘛！

这跑鞋技术我给满分
No.1｜中低技术

中底的泡沫密度比传统的鞋低了 30% 但是泡沫用量多出了1.5倍。

高回弹和足底反馈能给跑者提供更多弹力。比普通跑鞋多一层软垫，可以减缓大约80%的冲击力。

No.2｜船型设计

Hoka One One 把鞋底设计成了船形，典型的两种鞋底落差：分别是后跟 35mm 前掌 31mm；和后跟 30mm 前掌 24mm。

这种高度差能够给跑者带来特别流畅的跑步感受，就像踩着摇椅一样，跑起来轻松省力，能够减轻 20% 的膝盖压力。

No.3｜RMAT 中底

RMAT 技术是 Hoka 家的最新的技术，将弹性和坚固性相结合，同时还能配合大底提供一定的抓地性能。比一般 EVA 技术中底轻 30%。而且更软缓冲更好。

有了这个中底能够让你脚能更好的控制跑鞋，能在不同地形跑的更快，更轻松，更安全。

No.4｜增强外底

Hoka OneOne 比平常的鞋多加了 50% 的外底，针对如此厚的中底，大底采用了大量沟壑凹凸设计，来提高抓地力。同时鞋底的不同分区，保证灵活的同时不牺牲稳定性。不过即便如此，外地的抗磨损性能也不是特别好，容易消耗。

No.5｜速度鞋垫

许多款跑鞋都提供了两组鞋垫，塑形的速度鞋垫和裁断鞋垫。速度鞋垫采用3层接合技术让鞋垫保护脚更安全。并让脚掌更加贴合中底，保证最佳脚感。

裁断鞋垫使用了“OrthoLite"科技，轻量、防臭。在长途运动脚变肿胀之后，可以换更轻薄的裁断鞋垫，可以提高更宽敞的鞋内空间。

No.6｜快速鞋带

同样，多款跑鞋也配备了快速鞋带系统和普通鞋带，一按一拉就可以系上鞋带。方便在运动时换鞋或者换袜子。当然，你也可以换上普通鞋带，把鞋带系的更结实。

并且鞋子上有加宽的固定鞋口，提供包裹性之外，还能固定鞋带。

No.7｜“超纤”材料鞋舌

鞋舌采用了“超纤”材料制成，这种材料透气柔软，并且在鞋舌上的透气孔，可以保证鞋子的透气性。

该买哪款Hoka One One
哪种鞋子更适合你？不同的战况，采取不同的兵刃，大体以分这4类：

注：
1. 点图片可看美国价格，如需代购请联系微信kk794388，价格从优
2. 一款鞋子可能有很多种颜色/尺寸组合，需点入查看，图片只供参考
3. 关于美国鞋码的选择，请参考海淘鞋子尺码

全地形越野跑鞋，面向所有地形和不同路况，最大化的缓冲和保护，优异的通过性，在长距离耐力赛中，无与伦比的舒适性，明显提升了跑者的耐力和成绩。ATR系越野跑鞋，一直受入门级越野跑者和超级越野赛大神的追捧。

轻量化越野跑鞋，面向追求速度和竞技级大神的产物，最大化的缓冲，比路跑鞋还轻的重量，绝对是大神级的下坡神器。穿着Hoka One One的大神一次又一次登上各大赛事的领奖台绝非偶然。

越野跑/路跑两用跑鞋，面向城市越野跑，既满足日常慢跑训练需要，又能上山奔跑。与全地形越野跑鞋的区别在于，两用跑鞋的鞋底不仅更适合在硬化路面飞奔，而且在硬化路面上的耐磨表现更优秀。不过在复杂越野技术路段上的表现会逊色于全地形越野跑鞋。

长距离公路跑鞋，面向长距离的马拉松和路跑，最大化的缓冲，轻量化设计，无论是入门级跑者，还是争分夺秒的马拉松达人，都不会拒绝既轻量化，又不失缓冲和回弹力强的跑步神器。

参考：
- Wikipedia
- Sole Man: The Story Behind Hoka Shoes

= Hoka One One
:hp-image: https://cloud.githubusercontent.com/assets/19504323/15452702/48719838-202c-11e6-9c52-3d13d17bc909.jpg
:published_at: 2015-11-11
:hp-tags: Hoka, Hoka One One, Maximalist, 跑步, 跑鞋, 运动鞋, 缓震, 保护, 海淘, 亚马逊,

市面上的运动鞋可以说五花八门，但选择越多，越难选择。这里我为大家推荐一个牌子，叫Hoka One One。

这是一个极小众的牌子，但它的鞋子绝对是我穿过最舒服的，可以说秒杀Asics, NB, Mizuno, Saucony等一众顶级运动鞋品牌。

是什么让Hoka如此强大？

越野跑、铁人三项、马拉松，以及更虐更变态的耐力跑比赛中，都不断出现穿着HokaOneOne的选手。越来越多登上领奖台的选手选择HokaOneOne，一个只有旗舰级产品的品牌，一个每款产品都能独当一面的品牌，一个被誉为跑界路虎的品牌。...

在赤足跑流行的两年前，五指、裸足、0落差、薄底的跑鞋可火啦，崇尚自然的，利用肢体力量带来的缓冲作用进行运动，当时的阿迪、耐克、纽巴伦都被这种趋势牵着鼻子做跑鞋。

奇怪跑鞋火了

不过有这么一家名字奇怪，叫 Hoka One One 的小厂商，一反常态的跟流行趋势对着干。在裸足奔跑盛行的年代，开始耕耘厚底强缓震跑鞋。

据说其创始人跑到中国找化学家专门研发了一种轻质又缓震的泡沫材质。利用这种材质作为中底，生产出又大又缓震的超级越野跑鞋，简直是跑鞋界的路虎。

与其它跑鞋比一下，你就知道它有多大多厚了。

Hoka One One 是毛利语，相当于“To fly”的意思，翻译成中文意思是“别拦着我，我要上天”。

多数慢跑鞋的顶级跟次顶级的差别仅仅是在顶级跑鞋加入更多缓震技术，来适应大体重的用户，典型例子就是亚瑟士的顶级 Kayano 与次顶级 GT 系列。

Asics GT-2000

2013年，誓把缓震做到极致的 Hoka One One 被 Deckers 公司相中并且收购，这家公司同样是 UGG 的母公司。有没有很期待之后发生的事。

据说UGG鞋底用了Hoka技术

那时候，Hoka One One 只有90个店面，被收购之后这怪物跑鞋就迅速流行起来，两年之内就开了1200个店面。

软到像踩着“大白”跑步
Hoka One One 一门心思做厚厚的跑步鞋，这种鞋穿起来像踩在松糕上的感觉，特别适合上百甚至几百公里的超级马拉松等长距离运动。

对于这些距离长到快要逼近你极限的奔跑，穿 Hoka One One 跑鞋能带给你无敌的舒适感。

除了针对马拉松赛事这种公路跑产品之外，Hoka OneOne 还在研发越野跑系列，带你尽情翻山越岭。

其实，最初的产品就是为了应对下山这种巨伤膝盖的运动。

轻轻的 Hoka One One 可以背在包里，下山时换上，就不必担心你的膝盖受损了。

想象一下踩着大白的感觉。

这样优秀的跑鞋不出所料的迅速占领了各大跑鞋排行榜单。在最著名的《跑者世界》推荐榜单里经常出现它的身影。

比如 Hoka One One Tracer 是本年度春季跑鞋推荐中最轻量的跑鞋之一。

Hoka One One Tracer

《跑者世界》每年都会召集全美的成百上千名测试者对各种跑鞋进行测试并给出公正客观的评分，结合评分和实际脚感给出推荐。

我在看到《跑者世界》对 Hoka One One Conquest 公路跑鞋的测试分数时，全身打了个寒颤，我和我的小伙伴都惊呆了！

前掌缓震、后跟缓震、刚度评分几乎都要满分爆表，这是在造鞋么，简直是在造筋斗云嘛！

这跑鞋技术我给满分
No.1｜中低技术

中底的泡沫密度比传统的鞋低了 30% 但是泡沫用量多出了1.5倍。

高回弹和足底反馈能给跑者提供更多弹力。比普通跑鞋多一层软垫，可以减缓大约80%的冲击力。

No.2｜船型设计

Hoka One One 把鞋底设计成了船形，典型的两种鞋底落差：分别是后跟 35mm 前掌 31mm；和后跟 30mm 前掌 24mm。

这种高度差能够给跑者带来特别流畅的跑步感受，就像踩着摇椅一样，跑起来轻松省力，能够减轻 20% 的膝盖压力。

No.3｜RMAT 中底

RMAT 技术是 Hoka 家的最新的技术，将弹性和坚固性相结合，同时还能配合大底提供一定的抓地性能。比一般 EVA 技术中底轻 30%。而且更软缓冲更好。

有了这个中底能够让你脚能更好的控制跑鞋，能在不同地形跑的更快，更轻松，更安全。

No.4｜增强外底

Hoka OneOne 比平常的鞋多加了 50% 的外底，针对如此厚的中底，大底采用了大量沟壑凹凸设计，来提高抓地力。同时鞋底的不同分区，保证灵活的同时不牺牲稳定性。不过即便如此，外地的抗磨损性能也不是特别好，容易消耗。

No.5｜速度鞋垫

许多款跑鞋都提供了两组鞋垫，塑形的速度鞋垫和裁断鞋垫。速度鞋垫采用3层接合技术让鞋垫保护脚更安全。并让脚掌更加贴合中底，保证最佳脚感。

裁断鞋垫使用了“OrthoLite"科技，轻量、防臭。在长途运动脚变肿胀之后，可以换更轻薄的裁断鞋垫，可以提高更宽敞的鞋内空间。

No.6｜快速鞋带

同样，多款跑鞋也配备了快速鞋带系统和普通鞋带，一按一拉就可以系上鞋带。方便在运动时换鞋或者换袜子。当然，你也可以换上普通鞋带，把鞋带系的更结实。

并且鞋子上有加宽的固定鞋口，提供包裹性之外，还能固定鞋带。

No.7｜“超纤”材料鞋舌

鞋舌采用了“超纤”材料制成，这种材料透气柔软，并且在鞋舌上的透气孔，可以保证鞋子的透气性。

该买哪款Hoka One One
哪种鞋子更适合你？不同的战况，采取不同的兵刃，大体以分这4类：

注：
1. 点图片可看美国价格，如需代购请联系微信kk794388，价格从优
2. 一款鞋子可能有很多种颜色/尺寸组合，需点入查看，图片只供参考
3. 关于美国鞋码的选择，请参考海淘鞋子尺码

全地形越野跑鞋，面向所有地形和不同路况，最大化的缓冲和保护，优异的通过性，在长距离耐力赛中，无与伦比的舒适性，明显提升了跑者的耐力和成绩。ATR系越野跑鞋，一直受入门级越野跑者和超级越野赛大神的追捧。

轻量化越野跑鞋，面向追求速度和竞技级大神的产物，最大化的缓冲，比路跑鞋还轻的重量，绝对是大神级的下坡神器。穿着Hoka One One的大神一次又一次登上各大赛事的领奖台绝非偶然。

越野跑/路跑两用跑鞋，面向城市越野跑，既满足日常慢跑训练需要，又能上山奔跑。与全地形越野跑鞋的区别在于，两用跑鞋的鞋底不仅更适合在硬化路面飞奔，而且在硬化路面上的耐磨表现更优秀。不过在复杂越野技术路段上的表现会逊色于全地形越野跑鞋。

长距离公路跑鞋，面向长距离的马拉松和路跑，最大化的缓冲，轻量化设计，无论是入门级跑者，还是争分夺秒的马拉松达人，都不会拒绝既轻量化，又不失缓冲和回弹力强的跑步神器。

参考：
- Wikipedia
- Sole Man: The Story Behind Hoka Shoes


Inline images (3)

Inline images (3)

= Create A Kubernetes Cluster Using kops
:toc:
:icons:
:linkcss:
:imagesdir: ../../resources/images

This tutorial will walk you through how to install a Kubernetes cluster on AWS using kops.

https://github.com/kubernetes/kops[kops], short for Kubernetes Operations, is a set of tools for installing, operating, and deleting Kubernetes clusters. kops can also perform rolling upgrades from older versions of Kubernetes to newer ones. kops also manages the cluster add-ons. After the cluster is created, the link:../prereqs.adoc[Kubernetes CLI] can be used to manage resources in the cluster. If you have not gone through the link:../prereqs.adoc[Prerequisites] section, please do so now before continuing.

If you have not set up the link:../101-start-here[Cloud9 Devlopment Environment] yet, please do so before continuing.

== Create A Cluster with kops

The kops CLI can be used to create a highly available cluster, with multiple master nodes spread across multiple Availability Zones. Workers can be spread across multiple zones as well. Some of the tasks that happen behind the scene during cluster creation are:

- Provisioning EC2 instances
- Creating and configuring AWS resources such as a VPC, Auto Scaling Groups, IAM users, and security groups
- Installing Kubernetes
- If required configuring Route53 DNS

When setting up a cluster you have two options on how the nodes in the cluster communicate:

. <<create-a-gossip-based-kubernetes-cluster-with-kops, Using the gossip protocol>> - kops has support for a gossip-based cluster. This does not require a domain, subdomain, or Route53 hosted zone to be registered. A gossip-based cluster is therefore easier and quicker to setup, and is the preferred method for creating a cluster for use with this workshop.
. <<appendix-create-a-dns-based-kubernetes-cluster, Using DNS>> - Creating a Kubernetes cluster that uses DNS for node discovery requires your own domain (or subdomain) and setting up Route 53 hosted zones. This allows the various Kubernetes components to use DNS resolutions find and communicate with each other, and for kubectl to be able to talk directly with the master node(s).

Instructions for creating a gossip-based cluster are provided below, however, the examples in the workshop should work with either option. Instructions for creating a DNS-based cluster are provided as an appendix at the bottom of this page.

=== Create a Gossip Based Kubernetes Cluster with kops

kops supports creating a gossip-based cluster, which uses https://github.com/weaveworks/mesh[Weave Mesh] behind the scenes. This makes the process of creating a Kubernetes cluster using kops DNS-free, and therefore much simpler. This also means a top-level domain or a subdomain is no longer required to create the cluster. To create a cluster using the gossip protocol, indicate this to by using a cluster name with a suffix of `.k8s.local`. In the following steps, we will use example.cluster.k8s.local as a sample gossip cluster name. You may choose a different name as long as it ends with `.k8s.local`.

Information on setting up a DNS-based cluster can be found at the bottom of this page in the Appendix. However, setting up a gossip-based cluster allows you to get started quickly.

We show two examples of creating gossip-based clusters below. You can choose whether to create a single-master or multi-master cluster. Most workshop exercises will work on both types of cluster, however some modules require using a multi-master cluster (to demonstrate rolling updates, for instance.) If you aren't sure, please create a <<multi-master-multi-node-multi-az-gossip-based-cluster, multi-master cluster.>>

==== Default Gossip Based Cluster

By default, `create cluster` command creates a single master node and two worker nodes in the specified zones.

Create a Kubernetes cluster using the following command. This will create a cluster with a single master, multi-node and multi-az configuration:

    $ kops create cluster \
      --name example.cluster.k8s.local \
      --zones $AWS_AVAILABILITY_ZONES \
      --yes

The `AWS_AVAILABILITY_ZONES` environment variable should have been set during the link:../101-start-here[Cloud9 Environment Setup].

The `create cluster` command only creates and stores the cluster config in the S3 bucket. Adding the `--yes` flag ensures that the cluster is immediately created as well.

Alternatively, you may not specify the `--yes` flag as part of the `kops create cluster` command. Then you can use `kops edit cluster example.cluster.k8s.local` command to view the current cluster state and make changes. The cluster creation, in that case, is started with the following command:

    $ kops update cluster example.cluster.k8s.local --yes

Once the `kops create cluster` command is issued, it provisions the EC2 instances, sets up Auto Scaling Groups, IAM users, Security Groups, installs Kubernetes on each node, then configures the master and worker nodes. This process can take some time based upon the number of master and worker nodes.

Note: If your 'create cluster' fails with an error like:
```
error reading s3://example-kops-state-store-workshop/workshop-prep.cluster.k8s.local/config: Unable to list AWS regions: NoCredentialProviders: no valid providers in chain
caused by: EnvAccessKeyNotFound: failed to find credentials in the environment."
```
, try setting the following environment variables before executing the 'create cluster' command:
```
export AWS_DEFAULT_PROFILE=<your_aws_credentials_profile_name>
export AWS_SDK_LOAD_CONFIG=1
```

Wait for 5-8 minutes and then the cluster can be validated as shown:

```
$ kops validate cluster
Using cluster from kubectl context: example.cluster.k8s.local

Validating cluster example.cluster.k8s.local

INSTANCE GROUPS
NAME      ROLE  MACHINETYPE MIN MAX SUBNETS
master-eu-central-1a Master  m3.medium 1 1 eu-central-1a
nodes     Node  t2.medium 2 2 eu-central-1a,eu-central-1b

NODE STATUS
NAME        ROLE  READY
ip-172-20-57-94.ec2.internal  master  True
ip-172-20-63-55.ec2.internal  node  True
ip-172-20-75-78.ec2.internal  node  True

Your cluster example.cluster.k8s.local is ready
```
==== Multi-master, multi-node, multi-az Gossip Based Cluster

Create a cluster with multi-master, multi-node and multi-az configuration. We can create and build the cluster in one step by passing the `--yes` flag.

    $ kops create cluster \
      --name example.cluster.k8s.local \
      --master-count 3 \
      --node-count 5 \
      --zones $AWS_AVAILABILITY_ZONES \
      --yes

A multi-master cluster can be created by using the `--master-count` option and specifying the number of master nodes. An odd value is recommended. By default, the master nodes are spread across the AZs specified using the `--zones` option. Alternatively, `--master-zones` option can be used to explicitly specify the zones for the master nodes.

The `--zones` option is also used to distribute the worker nodes. The number of workers is specified using the `--node-count` option.

It will take 5-8 minutes for the cluster to be created. Validate the cluster:

```
$ kops validate cluster
Using cluster from kubectl context: example.cluster.k8s.local

Validating cluster example.cluster.k8s.local

INSTANCE GROUPS
NAME      ROLE  MACHINETYPE MIN MAX SUBNETS
master-eu-central-1a Master  m3.medium 1 1 eu-central-1a
master-eu-central-1b Master  m3.medium 1 1 eu-central-1b
master-eu-central-1c Master  c4.large  1 1 eu-central-1c
nodes     Node  t2.medium 5 5 eu-central-1a,eu-central-1b,eu-central-1c

NODE STATUS
NAME        ROLE  READY
ip-172-20-101-97.ec2.internal node  True
ip-172-20-119-53.ec2.internal node  True
ip-172-20-124-138.ec2.internal  master  True
ip-172-20-35-15.ec2.internal  master  True
ip-172-20-63-104.ec2.internal node  True
ip-172-20-69-241.ec2.internal node  True
ip-172-20-84-65.ec2.internal  node  True
ip-172-20-93-167.ec2.internal master  True

Your cluster example.cluster.k8s.local is ready
```

Note that all masters are spread across different AZs.

Your output may differ slightly from the one shown here based up on the type of cluster you created.

image:next-step-arrow.png[ , title="Continue!"]
You are now ready to continue on with the workshop!
The next step is link:../103-kubernetes-concepts[to learn about basic Kubernetes Concepts].

=== (Optional) Create a Kubernetes cluster in a private VPC

kops can create a private Kubernetes cluster, where the master and worker nodes are launched in private subnets in a VPC. This is possible with both Gossip and DNS-based clusters. This reduces the attack surface on your instances by protecting them behind security groups inside private subnets. The services hosted in the cluster can still be exposed via internet-facing ELBs if required. It's necessary to run a CNI network provider in the Kubernetes cluster when using a private topology. We have used https://www.projectcalico.org/[Calico] below, though other options such as `kopeio-vxlan`, `weave`, `romano` and others are available.

To print full list of CNI providers:

    kops create cluster --help

Create a gossip-based private cluster with master and worker nodes in private subnets:

    $ kops create cluster \
      --networking calico \
      --topology private \
      --name example.cluster.k8s.local \
      --zones $AWS_AVAILABILITY_ZONES \
      --yes

Once the `kops create cluster` command is issued, it provisions the EC2 instances, sets up AutoScaling Groups, IAM users, Security Groups, installs Kubernetes on each node, then configures the master and worker nodes. This process can take some time based upon the number of master and worker nodes.

Wait for 5-8 minutes and then the cluster can be validated as shown:

```
$ kops validate cluster
Using cluster from kubectl context: example.cluster.k8s.local

Validating cluster example.cluster.k8s.local

INSTANCE GROUPS
NAME                    ROLE    MACHINETYPE     MIN     MAX     SUBNETS
master-eu-central-1a    Master  m3.medium       1       1       eu-central-1a
nodes                   Node    t2.medium       2       2       eu-central-1a,eu-central-1b,eu-central-1c

NODE STATUS
NAME                                            ROLE    READY
ip-172-20-124-144.eu-central-1.compute.internal node    True
ip-172-20-58-179.eu-central-1.compute.internal  master  True
ip-172-20-93-220.eu-central-1.compute.internal  node    True

Your cluster example.cluster.k8s.local is ready
```

It is also possible to create a DNS-based cluster where the master and worker nodes are in private subnets. For more information about creating DNS-based clusters, see Appendix: Create a DNS-based Kubernetes cluster below.
If `--dns private` is also specified, a Route53 private hosted zone is created for routing the traffic for the domain within one or more VPCs. The Kubernetes API can therefore only be accessed from within the VPC. This is a current issue with kops (see https://github.com/kubernetes/kops/issues/2032). A possible workaround is to mirror the private Route53 hosted zone with a public hosted zone that exposes only the API server ELB endpoint. This workaround is discussed http://kubecloud.io/setup-ha-k8s-kops/[here].

Although most of the exercises in this workshop should work on a cluster with a private VPC, some commands won't, specifically those that use a proxy to access internally hosted services.

== Kubernetes Cluster Context

You may create multiple Kubernetes clusters. The configuration for each cluster is stored in a configuration file, referred to as "`kubeconfig file`". By default, kubectl looks for a file named `config` in the directory `~/.kube`. The kubectl CLI uses kubeconfig file to find the information it needs to choose a cluster and communicate with the API server of a cluster.

This allows you to deploy your applications to different environments by just changing the context. For example, here is a typical flow for application development:

. Build your application using minikube (See Set up Local Development Environment for more information)
. Change the context to a test cluster created on AWS
. Use the same command to deploy to test environment
. Once satisfied, change the context again to a production cluster on AWS
. Once again, use the same command to deploy to production environment

Get a summary of available contexts:

  $ kubectl config get-contexts
  kubectl config get-contexts
  CURRENT   NAME                          CLUSTER                     AUTHINFO                    NAMESPACE
  *         example.cluster.k8s.local     example.cluster.k8s.local   example.cluster.k8s.local
            minikube                      minikube                    minikube

The output shows dfferent contexts, one per cluster, that are available to kubectl. `NAME` column shows the context name. `*` indicates the current context.

View the current context:

  $ kubectl config current-context
  example.cluster.k8s.local

If multiple clusters exist, then you can change the context:

  $ kubectl config use-context <config-name>

== Turn on an API version for your Cluster

Note: This section is for Kubebernetes 1.7.x, in 1.8.x the api is `batch/v1beta1`.

Kubernetes resources are created with a specific API version. The exact value is defined by the `apiVersion` attribute in the resource configuration file. Some of the values are `v1`, `extensions/v1beta1` or `batch/v1`. By default, resources with `apiVersion` values X, Y, Z are enabled. If a resource has a version with the word `alpha` in it, then that version needs to be explicitly enabled in the cluster. For example, if you are running a Kubernetes cluster of version 1.7.x, then Cron Job resource cannot be created unless `batch/v2alpha1` is explicitly enabled.

This section shows how to turn on an API version for your cluster. It will use `batch/v2alpha1` as an example.

Specific API versions can be turned on or off by passing `--runtime-config=api/<version>` flag while bringing up the API server. To turn on our specific version, we'll need to pass `--runtime-config=batch/v2alpha1=true`.

For a cluster created using kops, this can be done by editing the cluster configuration using the command shown:

    $ kops edit cluster --name example.cluster.k8s.local

This will open up the cluster configuration in a text editor. Update the `spec` attribute such that it looks like as shown:

    spec:
      kubeAPIServer:
        runtimeConfig:
          batch/v2alpha1: "true"
      api:

Save the changes and exit the editor. Kubernetes cluster needs to re-read the configuration. This can be done by forcing a rolling update of the cluster using the following command:

NOTE: This process can easily take 30-45 minutes. Its recommended to leave the cluster without any updates during that time.

  $ kops rolling-update cluster --yes
  Using cluster from kubectl context: example.cluster.k8s.local

  NAME                    STATUS  NEEDUPDATE      READY   MIN     MAX     NODES
  master-eu-central-1a    Ready   0               1       1       1       1
  nodes                   Ready   0               2       2       2       2
  I1025 20:50:51.158013     354 instancegroups.go:350] Stopping instance "i-0ba714556f0f892cc", node "ip-172-20-58-179.eu-central-1.compute.internal", in AWS ASG "master-eu-central-1a.masters.example.cluster.k8s.local".
  I1025 20:55:51.413506     354 instancegroups.go:350] Stopping instance "i-0265a07c3320b266b", node "ip-172-20-93-220.eu-central-1.compute.internal", in AWS ASG "nodes.example.cluster.k8s.local".
  I1025 20:57:52.448582     354 instancegroups.go:350] Stopping instance "i-09e2efd9f5e9ebfce", node "ip-172-20-124-144.eu-central-1.compute.internal", in AWS ASG "nodes.example.cluster.k8s.local".
  I1025 20:59:53.325980     354 rollingupdate.go:174] Rolling update completed!

This command will first stop one master node in the cluster, re-read the configuration information and start that master. Then it will do the same for rest of the master nodes. And then it will repeat that for each worker node in the cluster. After all the server and worker nodes have been restarted, the rolling update of the cluster is complete.

Let's verify that the attributes are now successfully passed to the API server. Get the list of pods for the API server using the command shown:

  $ kubectl get pods --all-namespaces | grep kube-apiserver
  kube-system   kube-apiserver-ip-172-20-117-32.ec2.internal            1/1       Running   0          7m
  kube-system   kube-apiserver-ip-172-20-62-108.ec2.internal            1/1       Running   6          16m
  kube-system   kube-apiserver-ip-172-20-79-64.ec2.internal             1/1       Running   2          12m

The output shows three pods, one each for API server, corresponding to the three master nodes. This output is from a cluster with three master nodes. The output may be different if your cluster was created with different number of masters.

Search for the `--runtime-config` option as shown:

  $ kubectl describe --namespace=kube-system pod <pod-name> | grep runtime

`<pod-name>` is name of one of the pods shown above.

A formatted output is shown below:

  /usr/local/bin/kube-apiserver \
    --address=127.0.0.1 \
    --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,ResourceQuota \
    --allow-privileged=true \
    --anonymous-auth=false \
    --apiserver-count=3 \
    --authorization-mode=AlwaysAllow \
    --basic-auth-file=/srv/kubernetes/basic_auth.csv \
    --client-ca-file=/srv/kubernetes/ca.crt \
    --cloud-provider=aws \
    --etcd-servers-overrides=/events#http://127.0.0.1:4002 \
    --etcd-servers=http://127.0.0.1:4001 --insecure-port=8080 --kubelet-preferred-address-types=InternalIP,Hostname,ExternalIP \
    --runtime-config=batch/v2alpha1=true \
    --secure-port=443 \
    --service-cluster-ip-range=100.64.0.0/13 \
    --storage-backend=etcd2 \
    --tls-cert-file=/srv/kubernetes/server.cert \
    --tls-private-key-file=/srv/kubernetes/server.key \
    --token-auth-file=/srv/kubernetes/known_tokens.csv \
    --v=2 \
    1>>/var/log/kube-apiserver.log 2>&1

The output clearly shows that `--runtime-config=batch/v2alpha1=true` is passed as an option to the API server. This means the cluster is now ready for creating creating APIs with version `batch/v2alpha1`.

== Instance Groups with kops

An instance group, or ig for short, is a kops concept that defines a grouping of similar nodes. In AWS, an instance group maps to an Auto Scaling Group (ASG). Instructions on how to create instance groups can be found link:instance-groups/readme.adoc[here].

== Delete A Cluster

Any cluster can be deleted as shown:

    $ kops delete cluster \
      <cluster-name> \
      --yes

`<cluster-name>` is the name of the cluster. For example, our `example.cluster.k8s.local` cluster can be deleted as:

    $ kops delete cluster \
      example.cluster.k8s.local \
      --yes

If you leave off the `--yes` flag, you will get a listing of all the resources kops will delete.  To confirm deletion, run the command again appending `--yes`.

If you created a private VPC, then an additional cleanup of resources is required as shown below:

    # Find Route53 hosted zone ID from the console or via CLI and delete hosted zone
    aws route53 delete-hosted-zone --id $ZONEID
    # Delete VPC if you created earlier
    $ aws ec2 detach-internet-gateway --internet $IGW --vpc $VPCID
    aws ec2 delete-internet-gateway --internet-gateway-id $IGW
    aws ec2 delete-vpc --vpc-id $VPCID

To remove the state store S3 bucket:

    aws s3 rb $KOPS_STATE_STORE

== Appendix: Create a DNS-based Kubernetes cluster

To create a DNS-based Kubernetes cluster you'll need a top-level domain or subdomain that meets one of the following scenarios:

. Domain purchased/hosted via AWS
. A subdomain under a domain purchased/hosted via AWS
. Setting up Route53 for a domain purchased with another registrar, transfering the domain to Route53
. Subdomain for clusters in Route53, leaving the domain at another registrar

Then you need to follow the instructions in https://github.com/kubernetes/kops/blob/master/docs/aws.md#configure-dns[configure DNS]. Typically, the first and the last bullets are common scenarios.

==== Default DNS-based cluster

By default, `create cluster` command creates a single master node and two worker nodes in the specified zones.

Create a Kubernetes cluster using the following command. For the purposes of this demonstration, we will use a cluster name of example.cluster.com as our registered DNS. This will create a cluster with a single master, multi-node and multi-az configuration:

    $ kops create cluster \
      --name example.cluster.com \
      --zones $AWS_AVAILABILITY_ZONES \
      --yes

The `create cluster` command only creates and stores the cluster config in the S3 bucket. Adding `--yes` option ensures that the cluster is immediately created as well.

Alternatively, you may leave off the `--yes` option from the `kops create cluster` command. This will allow you to use `kops edit cluster example.cluster.com` command to view the current cluster state and make changes before actually creating the cluster.

The cluster creation, in that case, is started with the following command:

    $ kops update cluster example.cluster.com --yes

Once the `kops create cluster` or `kops update cluster` command is issued with the `--yes` flag,, it provisions the EC2 instances, setup Auto Scaling Groups, IAM users, security groups, and install Kubernetes on each node, configures master and worker nodes. This process can take a few minutes based upon the number of master and worker nodes.

Wait for 5-8 minutes and then the cluster can be validated as shown:

```
$ kops validate cluster --name=example.cluster.com
Validating cluster example.cluster.com

INSTANCE GROUPS
NAME      ROLE  MACHINETYPE MIN MAX SUBNETS
master-eu-central-1a Master  m3.medium 1 1 eu-central-1a
nodes     Node  t2.medium 2 2 eu-central-1a,eu-central-1b

NODE STATUS
NAME        ROLE  READY
ip-172-20-51-232.ec2.internal node  True
ip-172-20-60-192.ec2.internal master  True
ip-172-20-91-39.ec2.internal  node  True

Your cluster example.cluster.com is ready
```

Verify the client and server version:

  $ kubectl version
  Client Version: version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.1", GitCommit:"f38e43b221d08850172a9a4ea785a86a3ffa3b3a", GitTreeState:"clean", BuildDate:"2017-10-12T00:45:05Z", GoVersion:"go1.9.1", Compiler:"gc", Platform:"darwin/amd64"}
  Server Version: version.Info{Major:"1", Minor:"7", GitVersion:"v1.7.4", GitCommit:"793658f2d7ca7f064d2bdf606519f9fe1229c381", GitTreeState:"clean", BuildDate:"2017-08-17T08:30:51Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"}

It shows that Kubectl CLI version is 1.8.1 and the server version is 1.7.4. Cluster version may changed depending on kops version.

==== Multi-master, multi-node, multi-az DNS-based cluster

Check the list of Availability Zones that exist for your region using the following command:

    $ aws --region <region> ec2 describe-availability-zones

Create a cluster with multi-master, multi-node and multi-az configuration. We can create and build the cluster in
one step by passing the `--yes` flag.

    $ kops create cluster \
      --name example.cluster.com \
      --master-count 3 \
      --node-count 5 \
      --zones $AWS_AVAILABILITY_ZONES \
      --yes

A multi-master cluster can be created by using the `--master-count` option and specifying the number of master nodes. An odd value is recommended. By default, the master nodes are spread across the AZs specified using the `--zones` option. Alternatively, `--master-zones` option can be used to explicitly specify the zones for the master nodes.

`--zones` option is also used to distribute the worker nodes. The number of workers is specified using the `--node-count` option.

As mentioned above, wait for 5-8 minutes for the cluster to be created. Validate the cluster:

```
$ kops validate cluster --name=example.cluster.com
Validating cluster example.cluster.com

INSTANCE GROUPS
NAME      ROLE  MACHINETYPE MIN MAX SUBNETS
master-eu-central-1a Master  m3.medium 1 1 eu-central-1a
master-eu-central-1b Master  m3.medium 1 1 eu-central-1b
master-eu-central-1c Master  c4.large  1 1 eu-central-1c
nodes     Node  t2.medium 5 5 eu-central-1a,eu-central-1b,eu-central-1c

NODE STATUS
NAME        ROLE  READY
ip-172-20-103-30.ec2.internal master  True
ip-172-20-105-16.ec2.internal node  True
ip-172-20-127-147.ec2.internal  node  True
ip-172-20-35-38.ec2.internal  node  True
ip-172-20-47-199.ec2.internal node  True
ip-172-20-61-207.ec2.internal master  True
ip-172-20-75-78.ec2.internal  master  True
ip-172-20-94-216.ec2.internal node  True

Your cluster example.cluster.com is ready
```

Note that all masters are spread across different AZs.

Your output may differ from the one shown here based up on the type of cluster you created.

You are now ready to link:../103-kubernetes-concepts[continue on with the workshop.]


Add missing "Java Platform plugin" section in userguide

// Copyright 2018 the original author or authors.
//
// 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.

= Gradle User Manual: Version {gradleVersion}
:description: Single-page Gradle User Manual for Gradle {gradleVersion}
:meta-name-robots: noindex
:meta-name-twitter_card: summary
:meta-name-twitter_site: @gradle
:meta-name-twitter_creator: @gradle
:meta-name-twitter_title: {doctitle}
:meta-name-twitter_description: {description}
:meta-name-twitter_url: {docsUrl}/{gradleVersion}/userguide/{docname}.html
:meta-name-twitter_image: {website}/images/gradle-256x256.png

toc::[]

[[part:about_gradle]]
== About Gradle

include::overview.adoc[leveloffset=+2]

include::what_is_gradle.adoc[leveloffset=+2]

[[part:getting_started]]
== Getting Started

include::getting_started.adoc[leveloffset=2]

include::installation.adoc[leveloffset=+2]

include::troubleshooting.adoc[leveloffset=+2]

[[part:upgrading_and_migrating]]
== Upgrading and Migrating

include::upgrading_version_5.adoc[leveloffset=+2]
include::upgrading_version_4.adoc[leveloffset=+2]

include::migrating_from_ant.adoc[leveloffset=+2]

[[part:running_builds]]
== Running Gradle Builds

include::build_environment.adoc[leveloffset=+2]

include::gradle_daemon.adoc[leveloffset=+2]

include::init_scripts.adoc[leveloffset=+2]

include::intro_multi_project_builds.adoc[leveloffset=+2]

include::build_cache.adoc[leveloffset=+2]

include::composite_builds.adoc[leveloffset=+2]

[[part:authoring_builds]]
== Authoring Gradle Builds

include::tutorial_using_tasks.adoc[leveloffset=+2]

include::more_about_tasks.adoc[leveloffset=+2]

include::writing_build_scripts.adoc[leveloffset=+2]

include::working_with_files.adoc[leveloffset=+2]

include::custom_tasks.adoc[leveloffset=+2]

include::plugins.adoc[leveloffset=+2]

include::build_lifecycle.adoc[leveloffset=+2]

include::logging.adoc[leveloffset=+2]

include::multi_project_builds.adoc[leveloffset=+2]

include::organizing_gradle_projects.adoc[leveloffset=+2]

include::authoring_maintainable_build_scripts.adoc[leveloffset=+2]

include::lazy_configuration.adoc[leveloffset=+2]

include::test_kit.adoc[leveloffset=+2]

include::ant.adoc[leveloffset=+2]

[[part:dependency_management]]
== Dependency Management

include::introduction_dependency_management.adoc[leveloffset=+2]

include::dependency_management_terminology.adoc[leveloffset=+2]

include::dependency_types.adoc[leveloffset=+2]

include::repository_types.adoc[leveloffset=+2]

include::declaring_dependencies.adoc[leveloffset=+2]

include::declaring_repositories.adoc[leveloffset=+2]

include::inspecting_dependencies.adoc[leveloffset=+2]

include::managing_dependency_configurations.adoc[leveloffset=+2]

include::managing_transitive_dependencies.adoc[leveloffset=+2]

include::dependency_locking.adoc[leveloffset=+2]

include::troubleshooting_dependency_resolution.adoc[leveloffset=+2]

include::customizing_dependency_resolution_behavior.adoc[leveloffset=+2]

include::dependency_cache.adoc[leveloffset=+2]

include::working_with_dependencies.adoc[leveloffset=+2]

include::dependency_management_attribute_based_matching.adoc[leveloffset=+2]

[[part:publishing]]
== Publishing Artifacts

include::publishing_overview.adoc[leveloffset=+2]

include::artifact_management.adoc[leveloffset=+2]

[[part:jvm_projects]]
== Java & Other JVM Projects

include::building_java_projects.adoc[leveloffset=+2]

include::java_testing.adoc[leveloffset=+2]

include::dependency_management_for_java_projects.adoc[leveloffset=+2]

[[part:native_projects]]
== Native Projects

include::native_software.adoc[leveloffset=+2]

include::software_model_concepts.adoc[leveloffset=+2]

include::software_model.adoc[leveloffset=+2]

include::rule_source.adoc[leveloffset=+2]

include::software_model_extend.adoc[leveloffset=+2]

[[part:extending_gradle]]
== Extending Gradle

include::custom_plugins.adoc[leveloffset=+2]

include::java_gradle_plugin.adoc[leveloffset=+2]

include::embedding.adoc[leveloffset=+2]

[[part:reference]]
== Reference

include::groovy_build_script_primer.adoc[leveloffset=+2]

include::kotlin_dsl.adoc[leveloffset=+2]

include::plugin_reference.adoc[leveloffset=2]

include::command_line_interface.adoc[leveloffset=+2]

include::third_party_integration.adoc[leveloffset=2]

include::gradle_wrapper.adoc[leveloffset=+2]

include::directory_layout.adoc[leveloffset=+2]

[[part:plugins]]
== Plugins

include::antlr_plugin.adoc[leveloffset=+2]

include::application_plugin.adoc[leveloffset=+2]

include::base_plugin.adoc[leveloffset=+2]

include::build_init_plugin.adoc[leveloffset=+2]

include::checkstyle_plugin.adoc[leveloffset=+2]

include::codenarc_plugin.adoc[leveloffset=+2]

include::distribution_plugin.adoc[leveloffset=+2]

include::ear_plugin.adoc[leveloffset=+2]

include::eclipse_plugin.adoc[leveloffset=+2]

include::findbugs_plugin.adoc[leveloffset=+2]

include::groovy_plugin.adoc[leveloffset=+2]

include::idea_plugin.adoc[leveloffset=+2]

include::publishing_ivy.adoc[leveloffset=+2]

include::jacoco_plugin.adoc[leveloffset=+2]

include::java_plugin.adoc[leveloffset=+2]

include::java_library_plugin.adoc[leveloffset=+2]

include::java_library_distribution_plugin.adoc[leveloffset=+2]

include::java_platform_plugin.adoc[leveloffset=+2]

include::jdepend_plugin.adoc[leveloffset=+2]

include::publishing_maven.adoc[leveloffset=+2]

include::maven_plugin.adoc[leveloffset=+2]

include::osgi_plugin.adoc[leveloffset=+2]

include::play_plugin.adoc[leveloffset=+2]

include::pmd_plugin.adoc[leveloffset=+2]

include::scala_plugin.adoc[leveloffset=+2]

include::signing_plugin.adoc[leveloffset=+2]

include::war_plugin.adoc[leveloffset=+2]

== License Information

include::licenses.adoc[leveloffset=+1]

// Copyright 2018 the original author or authors.
//
// 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.

= Gradle User Manual: Version {gradleVersion}
:description: Single-page Gradle User Manual for Gradle {gradleVersion}
:meta-name-robots: noindex
:meta-name-twitter_card: summary
:meta-name-twitter_site: @gradle
:meta-name-twitter_creator: @gradle
:meta-name-twitter_title: {doctitle}
:meta-name-twitter_description: {description}
:meta-name-twitter_url: {docsUrl}/{gradleVersion}/userguide/{docname}.html
:meta-name-twitter_image: {website}/images/gradle-256x256.png

toc::[]

[[part:about_gradle]]
== About Gradle

include::overview.adoc[leveloffset=+2]

include::what_is_gradle.adoc[leveloffset=+2]

[[part:getting_started]]
== Getting Started

include::getting_started.adoc[leveloffset=2]

include::installation.adoc[leveloffset=+2]

include::troubleshooting.adoc[leveloffset=+2]

[[part:upgrading_and_migrating]]
== Upgrading and Migrating

include::upgrading_version_5.adoc[leveloffset=+2]
include::upgrading_version_4.adoc[leveloffset=+2]

include::migrating_from_ant.adoc[leveloffset=+2]

[[part:running_builds]]
== Running Gradle Builds

include::build_environment.adoc[leveloffset=+2]

include::gradle_daemon.adoc[leveloffset=+2]

include::init_scripts.adoc[leveloffset=+2]

include::intro_multi_project_builds.adoc[leveloffset=+2]

include::build_cache.adoc[leveloffset=+2]

include::composite_builds.adoc[leveloffset=+2]

[[part:authoring_builds]]
== Authoring Gradle Builds

include::tutorial_using_tasks.adoc[leveloffset=+2]

include::more_about_tasks.adoc[leveloffset=+2]

include::writing_build_scripts.adoc[leveloffset=+2]

include::working_with_files.adoc[leveloffset=+2]

include::custom_tasks.adoc[leveloffset=+2]

include::plugins.adoc[leveloffset=+2]

include::build_lifecycle.adoc[leveloffset=+2]

include::logging.adoc[leveloffset=+2]

include::multi_project_builds.adoc[leveloffset=+2]

include::organizing_gradle_projects.adoc[leveloffset=+2]

include::authoring_maintainable_build_scripts.adoc[leveloffset=+2]

include::lazy_configuration.adoc[leveloffset=+2]

include::test_kit.adoc[leveloffset=+2]

include::ant.adoc[leveloffset=+2]

[[part:dependency_management]]
== Dependency Management

include::introduction_dependency_management.adoc[leveloffset=+2]

include::dependency_management_terminology.adoc[leveloffset=+2]

include::dependency_types.adoc[leveloffset=+2]

include::repository_types.adoc[leveloffset=+2]

include::declaring_dependencies.adoc[leveloffset=+2]

include::declaring_repositories.adoc[leveloffset=+2]

include::inspecting_dependencies.adoc[leveloffset=+2]

include::managing_dependency_configurations.adoc[leveloffset=+2]

include::managing_transitive_dependencies.adoc[leveloffset=+2]

include::dependency_locking.adoc[leveloffset=+2]

include::troubleshooting_dependency_resolution.adoc[leveloffset=+2]

include::customizing_dependency_resolution_behavior.adoc[leveloffset=+2]

include::dependency_cache.adoc[leveloffset=+2]

include::working_with_dependencies.adoc[leveloffset=+2]

include::dependency_management_attribute_based_matching.adoc[leveloffset=+2]

[[part:publishing]]
== Publishing Artifacts

include::publishing_overview.adoc[leveloffset=+2]

include::artifact_management.adoc[leveloffset=+2]

[[part:jvm_projects]]
== Java & Other JVM Projects

include::building_java_projects.adoc[leveloffset=+2]

include::java_testing.adoc[leveloffset=+2]

include::dependency_management_for_java_projects.adoc[leveloffset=+2]

[[part:native_projects]]
== Native Projects

include::native_software.adoc[leveloffset=+2]

include::software_model_concepts.adoc[leveloffset=+2]

include::software_model.adoc[leveloffset=+2]

include::rule_source.adoc[leveloffset=+2]

include::software_model_extend.adoc[leveloffset=+2]

[[part:extending_gradle]]
== Extending Gradle

include::custom_plugins.adoc[leveloffset=+2]

include::java_gradle_plugin.adoc[leveloffset=+2]

include::embedding.adoc[leveloffset=+2]

[[part:reference]]
== Reference

include::groovy_build_script_primer.adoc[leveloffset=+2]

include::kotlin_dsl.adoc[leveloffset=+2]

include::plugin_reference.adoc[leveloffset=2]

include::command_line_interface.adoc[leveloffset=+2]

include::third_party_integration.adoc[leveloffset=2]

include::gradle_wrapper.adoc[leveloffset=+2]

include::directory_layout.adoc[leveloffset=+2]

[[part:plugins]]
== Plugins

include::antlr_plugin.adoc[leveloffset=+2]

include::application_plugin.adoc[leveloffset=+2]

include::base_plugin.adoc[leveloffset=+2]

include::build_init_plugin.adoc[leveloffset=+2]

include::checkstyle_plugin.adoc[leveloffset=+2]

include::codenarc_plugin.adoc[leveloffset=+2]

include::distribution_plugin.adoc[leveloffset=+2]

include::ear_plugin.adoc[leveloffset=+2]

include::eclipse_plugin.adoc[leveloffset=+2]

include::findbugs_plugin.adoc[leveloffset=+2]

include::groovy_plugin.adoc[leveloffset=+2]

include::idea_plugin.adoc[leveloffset=+2]

include::publishing_ivy.adoc[leveloffset=+2]

include::jacoco_plugin.adoc[leveloffset=+2]

include::java_plugin.adoc[leveloffset=+2]

include::java_library_plugin.adoc[leveloffset=+2]

include::java_library_distribution_plugin.adoc[leveloffset=+2]

include::jdepend_plugin.adoc[leveloffset=+2]

include::publishing_maven.adoc[leveloffset=+2]

include::maven_plugin.adoc[leveloffset=+2]

include::osgi_plugin.adoc[leveloffset=+2]

include::play_plugin.adoc[leveloffset=+2]

include::pmd_plugin.adoc[leveloffset=+2]

include::scala_plugin.adoc[leveloffset=+2]

include::signing_plugin.adoc[leveloffset=+2]

include::war_plugin.adoc[leveloffset=+2]

== License Information

include::licenses.adoc[leveloffset=+1]