OSDN Git Service

adds the latest docker-compose installation.
[metasearch/grid-chef-repo.git] / cookbooks / docker-grid / README.md
1 docker-grid Cookbook
2 ====================
3
4 This cookbook sets up Docker engine etc.
5
6 ## Contents
7
8 - [Requirements](#requirements)
9     - [platforms](#platforms)
10     - [packages](#packages)
11 - [Attributes](#attributes)
12 - [Usage](#usage)
13     - [Recipes](#recipes)
14         - [docker-grid::default](#docker-griddefault)
15         - [docker-grid::compose](#docker-gridcompose)
16         - [docker-grid::dind-compose](#docker-griddind-compose)
17         - [docker-grid::engine](#docker-gridengine)
18         - [docker-grid::registry](#docker-gridregistry)
19         - [docker-grid::registry-docker-compose](#docker-gridregistry-docker-compose)
20         - [docker-grid::registry-server](#docker-gridregistry-server)
21     - [Role Examples](#role-examples)
22     - [SSL server keys and certificates management by `ssl_cert` cookbook](#ssl-server-keys-and-certificates-management-by-ssl_cert-cookbook)
23 - [License and Authors](#license-and-authors)
24
25 ## Requirements
26
27 ### platforms
28 - CentOS, Red Hat Enterprise Linux >= 7.2 (in baremetal or LXD (Ubuntu >= 14.04))
29 - Debian >= 9.0
30 - Ubuntu >= 14.04 (in baremetal or LXD (Ubuntu >= 14.04))
31
32 ### packages
33 - none.
34
35 ## Attributes
36
37 |Key|Type|Description, example|Default|
38 |:--|:--|:--|:--|
39 |`['docker-grid']['install_flavor']`|String|`'dockerproject'` or `'os-repository'`|`'dockerproject'`|
40 |`['docker-grid']['dockerproject']['enable_new_repo']`|Boolean|flag to use the new repository.|`true`|
41 |`['docker-grid']['dockerproject']['apt_new_repo_sections']`|String|APT line's section. e.g. `'stable edge'`, `'edge test'`,...|`'stable'`|
42 |`['docker-grid']['dockerproject']['yum_new_repo_extra_enablerepo']`|String|e.g. `'docker-ce-edge,docker-ce-test'`|`''`|
43 |`['docker-grid']['dockerproject']['package_name']`|String|If the `'enable_new_repo'` is `true`, `'docker-ce'` will be automatically set.|`'docker-engine'`|
44 |`['docker-grid']['apt_repo']['url']`|String|If the `'enable_new_repo'` is `true`, the new repository URL will be automatically set.|`'https://apt.dockerproject.org/repo'`|
45 |`['docker-grid']['apt_repo']['keyserver']`|String|for the old repository only.|`'hkp://p80.pool.sks-keyservers.net:80'`|
46 |`['docker-grid']['apt_repo']['recv-keys']`|String|for the old repository only.|`'58118E89F3A912897C070ADBF76221572C52609D'`|
47 |`['docker-grid']['apt_repo']['override_apt_line']`|String|If you set this attribute, apt-line settings with the `['docker-grid']['apt_repo']['url']` attribute is overrridden. e.g. `'deb https://apt.dockerproject.org/repo ubuntu-xenial main'`|`''`|
48 |`['docker-grid']['yum_repo']['baseurl']`|String|for the old repository only.|`'https://yum.dockerproject.org/repo/main/centos/$releasever/'`|
49 |`['docker-grid']['yum_repo']['gpgcheck']`|String|for the old repository only. `'0'`: disabled, `'1'`: enabled.|`'1'`|
50 |`['docker-grid']['yum_repo']['gpgkey']`|String|for the old repository only.|`'https://yum.dockerproject.org/gpg'`|
51 |`['docker-grid']['compose']['install_flavor']`|String|`'dockerproject'` or `'os-repository'` or `'pypi'`|`'dockerproject'`|
52 |`['docker-grid']['compose']['skip_setup']`|Boolean||`false`|
53 |`['docker-grid']['compose']['auto_upgrade']`|Boolean|upgrade/reinstall the docker-compose automatically. Note: this flag is ignored in the case of `os-repository`.|`false`|
54 |`['docker-grid']['compose']['version']`|String|Note: this version is ignored in the case of `os-repository`. `''` (empty) means the latest version. This version is ignored in the case of `'os-repository'`.|`'1.21.1'`|
55 |`['docker-grid']['compose']['release_base_url']`|String||`"https://github.com/docker/compose/releases/download/#{['docker-grid']['compose']['version']}"`|
56 |`['docker-grid']['compose']['release_url']`|String||`"#{node['docker-grid']['compose']['release_base_url']}/docker-compose-#{node['kernel']['name']}-#{node['kernel']['machine']}"`|
57 |`['docker-grid']['compose']['home_dir']`|String||`'/opt/docker-compose'`|
58 |`['docker-grid']['compose']['app_dir']`|String||`"#{node['docker-grid']['compose']['home_dir']}/app"`|
59 |`['docker-grid']['dind-compose']['app_dir']`|String|docker-compose application root directory for Docker in Docker.|`"#{node['docker-grid']['compose']['app_dir']}/docker-in-docker"`|
60 |`['docker-grid']['dind-compose']['data_dir']`|String|persistent data directory.|`"#{node['docker-grid']['dind-compose']['app_dir']}/data"`|
61 |`['docker-grid']['dind-compose']['config']`|Hash|`docker-compose.yml` configurations.|See `attributes/default.rb`|
62 |`['docker-grid']['engine']['skip_setup']`|Boolean||`false`|
63 |`['docker-grid']['engine']['version_on_centos']`|String|Docker version for CentOS. `''` (empty) means the latest version.|`'17.12.1.ce-1'`|
64 |`['docker-grid']['engine']['version_on_debian']`|String|Docker version for Debian. `''` (empty) means the latest version.|`'17.12.1~ce-0'`|
65 |`['docker-grid']['engine']['version_on_ubuntu']`|String|Docker version for Ubuntu. `''` (empty) means the latest version.|`'17.12.1~ce-0'`|
66 |`['docker-grid']['engine']['version']`|String|Docker **exact** version. `''` (empty) or `'latest'` means the latest version. Note: this **default** value is overwritten by the `version_on_{centos or ubuntu}`. But if you would override this attribute once, the `version_on_{centos or ubuntu}` values are ignored.|See default.rb|
67 |`['docker-grid']['engine']['storage-driver_on_centos']`|String|Docker storage driver (overlay, devicemapper, ...) for CentOS.|`'overlay'`|
68 |`['docker-grid']['engine']['storage-driver_on_debian']`|String|Docker storage driver (aufs, overlay, ...) for Debian.|`'overlay2'`|
69 |`['docker-grid']['engine']['storage-driver_on_ubuntu']`|String|Docker storage driver (aufs, overlay, ...) for Ubuntu.|`'aufs'`|
70 |`['docker-grid']['engine']['storage-driver']`|String||See default.rb|
71 |`['docker-grid']['engine']['userns-remap']`|String|e.g. `'default'` (`dockremap` user/group) or your specified user/group name. Note: it is available in Docker 1.10/later and (Ubuntu or RHEL family 7.2/later).|`nil` (inactive)|
72 |`['docker-grid']['engine']['daemon_extra_options']`|String|ref. `docker daemon --help`.|`'-H fd://'`|
73 |`['docker-grid']['engine']['users_allow']`|Array|Non-root users allowed to manage Docker daemon.|`[]`|
74 |`['docker-grid']['registry']['with_ssl_cert_cookbook']`|Boolean|If this attribute is true, `node['docker-grid']['registry']['docker-compose']['config']` are are overridden by the following `common_name` attributes.|`false`|
75 |`['docker-grid']['registry']['ssl_cert']['common_name']`|String|Registry server common name for TLS|`node['fqdn']`|
76 |`['docker-grid']['registry']['server']['config']`|Hash|Registry server configurations.|See `attributes/default.rb`|
77 |`['docker-grid']['registry']['docker-compose']['app_dir']`|String||`"#{node['docker-grid']['compose']['app_dir']}/registry"`|
78 |`['docker-grid']['registry']['docker-compose']['host_data_volume']`|String|Data directory path on the host filesystem or `nil` (unset).|`'/var/lib/docker-registry'`|
79 |`['docker-grid']['registry']['docker-compose']['config_format_version']`|String|`docker-compose.yml` format version. `'1'` or `'2'`|`'1'`|
80 |`['docker-grid']['registry']['docker-compose']['service_name']`|String|Docker registry service name in the `docker-compose.yml`|`'registry'`|
81 |`['docker-grid']['registry']['docker-compose']['config']`|Hash|`docker-compose.yml` configurations. See attributes/default.rb and [_Deploying a registry server_](https://docs.docker.com/registry/deploying/#/managing-with-compose) |See `attributes/default.rb`|
82 |`['docker-grid']['registry']['docker-compose']['registry-config']`|Hash|See [_Overriding the entire configuration file_](https://docs.docker.com/registry/configuration/#/overriding-the-entire-configuration-file)|`nil`|
83
84 ## Usage
85
86 ### Recipes
87
88 #### docker-grid::default
89
90 This recipe does nothing.
91
92 #### docker-grid::compose
93
94 This recipe installs docker-compose.
95
96 #### docker-grid::dind-compose
97
98 This recipe sets up Docker Compose configurations for a Docker in Docker service.
99
100 #### docker-grid::engine
101
102 This recipe sets up Docker engine.
103
104 #### docker-grid::registry
105
106 This recipe sets up Docker Compose configurations for the Docker registry service.
107
108 #### docker-grid::registry-docker-compose
109
110 This recipe is alias of the `docker-grid::registry` recipe.
111
112 #### docker-grid::registry-server
113
114 This recipe sets up a Docker registry service on real host.
115
116 ### Role Examples
117
118 - `roles/docker-new-repo.rb`:  installs the `docker-ce` package by the new repository.
119
120 ```ruby
121 name 'docker-new-repo'
122 description 'Docker CE by the new repository'
123
124 run_list(
125   'role[docker]',
126 )
127
128 override_attributes(
129   'docker-grid' => {
130     'install_flavor' => 'dockerproject',
131     'dockerproject' => {
132       'enable_new_repo' => true,
133     },
134     'compose' => {
135       #'skip_setup' => true,  # default: false
136       'auto_upgrade' => true,  # default: false
137       'release_base_url' => 'https://github.com/docker/compose/releases/download/1.17.1',
138     },
139     'engine' => {
140       'version' => '',  # latest
141       #'skip_setup' => true,  # default: false
142       # new package: `docker-ce`
143       #'version_on_centos' => '17.09.0.ce-1',
144       #'version_on_ubuntu' => '17.05.0~ce-0',
145       'storage-driver_on_centos' => 'devicemapper',
146       'storage-driver_on_ubuntu' => 'overlay2',  # default: aufs
147     },
148   },
149 )
150 ```
151
152 - `roles/docker.rb`:  installs the `docker-engine` package by the old repository.
153
154 ```ruby
155 name 'docker'
156 description 'Docker Engine distributed by dockerproject'
157
158 run_list(
159   'recipe[docker-grid::engine]',
160 )
161
162 override_attributes(
163   'docker-grid' => {
164     'install_flavor' => 'dockerproject',
165     'engine' => {
166       'version_on_centos' => '17.03.1.ce-1',
167       'version_on_debian' => '17.03.1~ce-0',
168       'version_on_ubuntu' => '17.03.1~ce-0',
169       'storage-driver_on_centos' => 'overlay',
170       'storage-driver_on_debian' => 'overlay2',
171       'storage-driver_on_ubuntu' => 'overlay2',  # default: 'aufs'
172       #'userns-remap' => 'default',  # default: nil (inactive)
173       'daemon_extra_options' => '-H fd:// --bip=192.168.128.1/24 --fixed-cidr=192.168.128.0/24',
174     },
175   },
176 )
177 ```
178
179 - `roles/docker4latest_ubuntu.rb`:  installs the `docker-ce` package to the latest Ubuntu.
180
181 ```ruby
182 name 'docker4latest_ubuntu'
183 description 'Docker for the latest Ubuntu'
184
185 run_list(
186   'role[docker]',
187 )
188
189 override_attributes(
190   'docker-grid' => {
191     'install_flavor' => 'dockerproject',
192     'dockerproject' => {
193       'enable_new_repo' => true,
194       'package_name' => 'docker-ce',  # new package name.
195     },
196     # install the package for the newer distribution of ubuntu.
197     'apt_repo' => {
198       # new repo.
199       #'override_apt_line' => 'deb [arch=amd64] https://download.docker.com/linux/ubuntu artful stable',  # not active yet
200       'override_apt_line' => 'deb [arch=amd64] https://download.docker.com/linux/ubuntu zesty stable',
201       # old repo.
202       #'override_apt_line' => 'deb https://apt.dockerproject.org/repo ubuntu-zesty main',
203       #'override_apt_line' => 'deb https://apt.dockerproject.org/repo ubuntu-xenial main',
204     },
205     'compose' => {
206       #'skip_setup' => true,  # default: false
207       'auto_upgrade' => true,  # default: false
208       'release_base_url' => 'https://github.com/docker/compose/releases/download/1.17.1',
209     },
210     'engine' => {
211       # new package: `docker-ce``
212       'version' => '17.09.0~ce-0~ubuntu',
213       #'version' => '17.06.2~ce-0~ubuntu',
214       # old package: `docker-engine``
215       #'version' => '17.05.0~ce-0~ubuntu-zesty',
216       #'version' => '17.03.1~ce-0~ubuntu-yakkety',
217       #'version' => '1.12.3-0~xenial',
218       'storage-driver_on_ubuntu' => 'overlay2',  # default: aufs
219     },
220   },
221 )
222 ```
223
224 - `roles/docker-rhel.rb`:  installs the `docker` package.
225
226 ```ruby
227 name 'docker-rhel'
228 description 'Docker Engine distributed by RHEL'
229
230 run_list(
231   'recipe[docker-grid::engine]',
232 )
233
234 override_attributes(
235   'docker-grid' => {
236     'install_flavor' => 'os-repository',
237     'engine' => {
238       'version_on_centos' => '1.12.5-14',  # docker package
239       'version_on_ubuntu' => '1.12.3-0ubuntu4~16.04.2',  # docker.io package
240       'storage-driver_on_centos' => 'overlay',
241       'storage-driver_on_ubuntu' => 'overlay',  # default: aufs
242       #'userns-remap' => 'default',
243       'daemon_extra_options' => '-H fd://',
244       # for RHEL docker package >= 1.12: '-H fd://' option automatically removed by this cookbook.
245       # See https://github.com/docker/docker/issues/22847
246     },
247   },
248 )
249 ```
250
251 - `roles/docker-ubuntu.rb`: installs the `docker.io` package.
252
253 ```ruby
254 name 'docker-ubuntu'
255 description 'Docker Engine distributed by Ubuntu'
256
257 run_list(
258   'recipe[docker-grid::engine]',
259 )
260
261 override_attributes(
262   'docker-grid' => {
263     'install_flavor' => 'os-repository',
264     'engine' => {
265       'version_on_centos' => '1.12.5-14',  # docker package
266       'version_on_ubuntu' => '1.12.3-0ubuntu4~16.04.2',  # docker.io package
267       'storage-driver_on_centos' => 'overlay',
268       'storage-driver_on_ubuntu' => 'overlay',  # default: aufs
269       #'userns-remap' => 'default',
270       'daemon_extra_options' => '-H fd://',
271     },
272   },
273 )
274 ```
275
276 - `roles/docker-registry.rb`: on Docker.
277
278 ```ruby
279 name 'docker-registry'
280 description 'Docker Registry Server'
281
282 run_list(
283   'recipe[docker-grid::registry]',
284 )
285
286 override_attributes(
287   'docker-grid' => {
288     'engine' => {
289       'version_on_centos' => '17.03.1.ce-1',
290       'version_on_debian' => '17.03.1~ce-0',
291       'version_on_ubuntu' => '17.03.1~ce-0',
292       'storage-driver_on_centos' => 'overlay',
293       'storage-driver_on_debian' => 'overlay2',
294       'storage-driver_on_ubuntu' => 'overlay2',  # default: 'aufs'
295       'userns-remap' => '',
296       'daemon_extra_options' => \
297         '-H fd:// --bip=192.168.128.1/24 --fixed-cidr=192.168.128.0/24', \
298         # for development environment only.
299         #+ ' --insecure-registry registry.docker.example.com:5000',
300     },
301     'registry' => {
302       'docker-compose' => {
303         'config_format_version' => '1',
304         'host_data_volume' => nil,
305         'config' => {
306           # in docker-compose.yml
307           # See: https://docs.docker.com/registry/deploying/#/managing-with-compose
308           'registry' => {
309             'restart' => 'always',
310             'image' => 'registry:2',
311             'ports' => [
312               '5000:5000',
313             ],
314             'environment' => {
315               'REGISTRY_HTTP_TLS_CERTIFICATE' => '/certs/domain.crt',
316               'REGISTRY_HTTP_TLS_KEY' =>         '/certs/domain.key',
317               'REGISTRY_AUTH' =>                'htpasswd',
318               'REGISTRY_AUTH_HTPASSWD_PATH' =>  '/auth/htpasswd',
319               'REGISTRY_AUTH_HTPASSWD_REALM' => 'Registry Realm',
320             },
321             'volumes' => [
322               '/path/data:/var/lib/registry',
323               '/path/certs:/certs',
324               '/path/auth:/auth',
325             ],
326           },
327         },
328       },
329     },
330   },
331 )
332 ```
333
334 - `roles/docker-registry-with-ssl-cert.rb`: on Docker.
335
336 ```ruby
337 name 'docker-registry-with-ssl-cert'
338 description 'Docker Registry Server'
339
340 registry_fqdn = 'registry.docker.example.com'
341
342 run_list(
343   #'recipe[ssl_cert::server_key_pairs]',  # docker-grid <= 0.3.9
344   'recipe[docker-grid::registry]',
345 )
346
347 override_attributes(
348   'ssl_cert' => {
349     'common_names' => [
350       registry_fqdn,
351     ],
352   },
353   'docker-grid' => {
354     'engine' => {
355       'version_on_centos' => '17.03.1.ce-1',
356       'version_on_debian' => '17.03.1~ce-0',
357       'version_on_ubuntu' => '17.03.1~ce-0',
358       'storage-driver_on_centos' => 'overlay',
359       'storage-driver_on_debian' => 'overlay2',
360       'storage-driver_on_ubuntu' => 'overlay2',  # default: 'aufs'
361       'userns-remap' => '',
362       'daemon_extra_options' => \
363         '-H fd:// --bip=192.168.128.1/24 --fixed-cidr=192.168.128.0/24',
364     },
365     'registry' => {
366       'with_ssl_cert_cookbook' => true,
367       'ssl_cert' => {
368         'common_name' => registry_fqdn,
369       },
370       'docker-compose' => {
371         'config_format_version' => '1',
372         'host_data_volume' => nil,
373         'config' => {
374           # in docker-compose.yml
375           # See: https://docs.docker.com/registry/deploying/#/managing-with-compose
376           'registry' => {
377             'restart' => 'always',
378             'image' => 'registry:2',
379             'ports' => [
380               '5000:5000',
381             ],
382             'environment' => {
383               # REGISTRY_HTTP_TLS_{CERTIFICATE,KEY} will be set automatically.
384               'REGISTRY_AUTH' =>                'htpasswd',
385               'REGISTRY_AUTH_HTPASSWD_PATH' =>  '/auth/htpasswd',
386               'REGISTRY_AUTH_HTPASSWD_REALM' => 'Registry Realm',
387               # proxy cache
388               #'REGISTRY_PROXY_REMOTEURL' => 'https://registry-1.docker.io',
389             },
390             'volumes' => [
391               # Volumes for the server certificate and key files will be set automatically.
392               '/path/data:/var/lib/registry',
393               '/path/auth:/auth',
394             ],
395           },
396         },
397       },
398     },
399   },
400 )
401 ```
402
403 - `roles/docker-registry-by-entire-config.rb`: on Docker.
404
405 ```ruby
406 name 'docker-registry-by-entire-config'
407 description 'Docker Registry Server'
408
409 run_list(
410   'recipe[docker-grid::registry]',
411 )
412
413 override_attributes(
414   'docker-grid' => {
415     'engine' => {
416       'version_on_centos' => '17.03.1.ce-1',
417       'version_on_debian' => '17.03.1~ce-0',
418       'version_on_ubuntu' => '17.03.1~ce-0',
419       'storage-driver_on_centos' => 'overlay',
420       'storage-driver_on_debian' => 'overlay2',
421       'storage-driver_on_ubuntu' => 'overlay2',  # default: 'aufs'
422       'userns-remap' => '',
423       'daemon_extra_options' => \
424         '-H fd:// --bip=192.168.128.1/24 --fixed-cidr=192.168.128.0/24', \
425         # for development environment only.
426         #+ ' --insecure-registry registry.docker.example.com:5000',
427     },
428     'registry' => {
429       'docker-compose' => {
430         'registry-config' => {
431           # NOT nil
432           # in ./etc/config.yml
433           # See: https://docs.docker.com/registry/configuration/#/overriding-the-entire-configuration-file
434           'version' => '0.1',
435           # ...
436         },
437         'config_format_version' => '1',
438         'config' => {
439           # in ./docker-compose.yml
440           # See: https://docs.docker.com/registry/deploying/#/managing-with-compose
441           'registry' => {
442             'restart' => 'always',
443             'image' => 'registry:2',
444             'ports' => [
445               '5000:5000',
446             ],
447             'environment' => {
448               # -> ./etc/config.yml
449             },
450             'volumes' => [
451               # Volumes for the ./etc/config.yml will be set automatically.
452               #'./etc/config.yml:/etc/docker/registry/config.yml:ro',
453               '/path/data:/var/lib/registry',
454               '/path/auth:/auth',
455             ],
456           },
457         },
458       },
459     },
460   },
461 )
462 ```
463
464 - `roles/registry-server-with-ssl-cert.rb`: on real host.
465
466 ```ruby
467 name 'registry-server-with-ssl-cert'
468 description 'Docker Registry Server'
469
470 registry_fqdn = 'registry.docker.example.com'
471
472 run_list(
473   'recipe[docker-grid::registry-server]',
474 )
475
476 override_attributes(
477   'ssl_cert' => {
478     'common_names' => [
479       registry_fqdn,
480     ],
481   },
482   'docker-grid' => {
483     'registry' => {
484       'with_ssl_cert_cookbook' => true,
485       'ssl_cert' => {
486         'common_name' => registry_fqdn,
487       },
488       'server' => {
489         'config' => {
490           'storage' => {
491             'filesystem' => {
492               'rootdirectory' => '/var/lib/docker-registry',
493             },
494           },
495           'proxy' => {
496             'remoteurl' => 'https://registry-1.docker.io',
497           },
498         },
499       },
500     },
501   },
502 )
503 ```
504
505 ### SSL server keys and certificates management by `ssl_cert` cookbook
506
507 - create vault items.
508
509 ```text
510 $ ruby -rjson -e 'puts JSON.generate({"private" => File.read("registry.docker.example.com.prod.key")})' \
511 > > ~/tmp/registry.docker.example.com.prod.key.json
512
513 $ ruby -rjson -e 'puts JSON.generate({"public" => File.read("registry.docker.example.com.prod.crt")})' \
514 > > ~/tmp/registry.docker.example.com.prod.crt.json
515
516 $ cd $CHEF_REPO_PATH
517
518 $ knife vault create ssl_server_keys registry.docker.example.com.prod \
519 > --json ~/tmp/registry.docker.example.com.prod.key.json
520
521 $ knife vault create ssl_server_certs registry.docker.example.com.prod \
522 > --json ~/tmp/registry.docker.example.com.prod.crt.json
523 ```
524
525 - grant reference permission to the Docker Registry host
526
527 ```text
528 $ knife vault update ssl_server_keys  registry.docker.example.com.prod -S 'name:registry-host.example.com'
529 $ knife vault update ssl_server_certs registry.docker.example.com.prod -S 'name:registry-host.example.com'
530 ```
531
532 - modify run_list and attributes
533
534 ```ruby
535 run_list(
536   #'recipe[ssl_cert::server_key_pairs]',  # docker-grid <= 0.3.9
537   'recipe[docker-grid::registry]',
538 )
539
540 override_attributes(
541   'ssl_cert' => {
542     'common_names' => [
543       'registry.docker.example.com',
544     ],
545   },
546   'docker-grid' => {
547     'registry' => {
548       'with_ssl_cert_cookbook' => true,
549       'ssl_cert' => {
550         'common_name' => 'registry.docker.example.com',
551       },
552       # ...
553     },
554   },
555 )
556 ```
557
558 ## License and Authors
559
560 - Author:: whitestar at osdn.jp
561
562 ```text
563 Copyright 2016-2017, whitestar
564
565 Licensed under the Apache License, Version 2.0 (the "License");
566 you may not use this file except in compliance with the License.
567 You may obtain a copy of the License at
568
569     http://www.apache.org/licenses/LICENSE-2.0
570
571 Unless required by applicable law or agreed to in writing, software
572 distributed under the License is distributed on an "AS IS" BASIS,
573 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
574 See the License for the specific language governing permissions and
575 limitations under the License.
576 ```