kubes 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +4 -0
  3. data/CHANGELOG.md +18 -1
  4. data/Dockerfile +14 -0
  5. data/docs/_docs/auto-context.md +4 -4
  6. data/docs/_docs/ci/cloudbuild.md +69 -0
  7. data/docs/_docs/config/builder.md +46 -0
  8. data/docs/_docs/config/env.md +3 -3
  9. data/docs/_docs/dsl/resources/deployment.md +0 -5
  10. data/docs/_docs/dsl/resources/network_policy.md +1 -1
  11. data/docs/_docs/dsl/resources/service.md +21 -5
  12. data/docs/_docs/helpers.md +2 -2
  13. data/docs/_docs/learn/dsl/deploy.md +2 -0
  14. data/docs/_docs/learn/dsl/new-project.md +1 -2
  15. data/docs/_docs/learn/yaml/deploy.md +2 -0
  16. data/docs/_docs/learn/yaml/new-project.md +1 -1
  17. data/docs/_docs/learn/yaml/review-project.md +0 -5
  18. data/docs/_docs/yaml.md +0 -5
  19. data/docs/_includes/intro/install.md +5 -1
  20. data/docs/_includes/learn/cluster.md +21 -4
  21. data/docs/_includes/learn/repo +0 -0
  22. data/docs/_includes/learn/repos.md +10 -0
  23. data/docs/_includes/learn/review.md +2 -2
  24. data/docs/_includes/sidebar.html +6 -0
  25. data/docs/_reference/kubes-apply.md +2 -2
  26. data/docs/_reference/kubes-delete.md +1 -1
  27. data/docs/_reference/kubes-deploy.md +1 -1
  28. data/docs/_reference/kubes-describe.md +24 -0
  29. data/docs/_reference/kubes-exec.md +42 -0
  30. data/docs/_reference/kubes-get.md +27 -0
  31. data/docs/_reference/kubes-init.md +1 -1
  32. data/docs/_reference/kubes-logs.md +26 -0
  33. data/docs/bin/web +1 -1
  34. data/docs/reference.md +4 -0
  35. data/lib/kubes/cli.rb +21 -0
  36. data/lib/kubes/cli/apply.rb +1 -1
  37. data/lib/kubes/cli/base.rb +4 -0
  38. data/lib/kubes/cli/build.rb +2 -2
  39. data/lib/kubes/cli/delete.rb +7 -2
  40. data/lib/kubes/cli/describe.rb +1 -1
  41. data/lib/kubes/cli/docker.rb +2 -2
  42. data/lib/kubes/cli/exec.rb +33 -0
  43. data/lib/kubes/cli/get.rb +3 -1
  44. data/lib/kubes/cli/help/exec.md +17 -0
  45. data/lib/kubes/cli/logs.rb +13 -0
  46. data/lib/kubes/compiler/dsl/syntax/deployment.rb +77 -10
  47. data/lib/kubes/compiler/dsl/syntax/service.rb +11 -0
  48. data/lib/kubes/compiler/shared/helpers.rb +2 -1
  49. data/lib/kubes/config.rb +2 -0
  50. data/lib/kubes/docker.rb +19 -0
  51. data/lib/kubes/docker/strategy/build/base.rb +24 -0
  52. data/lib/kubes/docker/strategy/build/docker.rb +11 -0
  53. data/lib/kubes/docker/strategy/build/gcloud.rb +10 -0
  54. data/lib/kubes/docker/strategy/hooks.rb +9 -0
  55. data/lib/kubes/docker/{base.rb → strategy/image_name.rb} +19 -32
  56. data/lib/kubes/docker/strategy/push/base.rb +9 -0
  57. data/lib/kubes/docker/{push.rb → strategy/push/docker.rb} +2 -5
  58. data/lib/kubes/docker/strategy/push/gcloud.rb +9 -0
  59. data/lib/kubes/docker/strategy/utils.rb +9 -0
  60. data/lib/kubes/hooks/builder.rb +2 -1
  61. data/lib/kubes/kubectl.rb +15 -3
  62. data/lib/kubes/kubectl/batch.rb +8 -1
  63. data/lib/kubes/kubectl/fetch/base.rb +24 -0
  64. data/lib/kubes/kubectl/fetch/deployment.rb +34 -0
  65. data/lib/kubes/kubectl/fetch/pods.rb +21 -0
  66. data/lib/kubes/util/sh.rb +1 -0
  67. data/lib/kubes/version.rb +1 -1
  68. data/lib/templates/dsl/.kubes/resources/web/deployment.rb +3 -1
  69. data/lib/templates/dsl/.kubes/resources/web/service.rb +1 -1
  70. data/lib/templates/yaml/.kubes/resources/web/deployment.yaml.tt +0 -5
  71. metadata +28 -5
  72. data/lib/kubes/docker/build.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5115dab9c9c9e52678c67ba1c6a956ffe9369961555d6393e8ad0ca40868fdd
4
- data.tar.gz: 6f04422236a9df6a5833633c0fdc2b1bc49d31786c0fd1f1f599a75a7b7771eb
3
+ metadata.gz: f6a93c63c1e8d4aeea46969a491b3065c97e9a1a290bfd871286e794c35cb0d6
4
+ data.tar.gz: d8dbb91a1214bf8c42a5c9dd1e591dfcf6c41ffee6feb36af0af2f7b3a519ebd
5
5
  SHA512:
6
- metadata.gz: c60e4d318db13f130929e1e554b93594f23cfda75b3a2e6974f395b1c1440734077bd85d9e11f0460371f62d8810e402db493c7fdf86b3f823ca8eb8810bb32e
7
- data.tar.gz: b344b7af0da2964097d564407402cac1b880d134b1d213f1a40deb0234e661c7e08dc15fa3005fcfd4ba6f714d4262f5bd25c17fc876c545d0fac41898cb2a86
6
+ metadata.gz: dfc13591d144fb99837297c4c766354ceed410df631e505e54517e9759b9b44b03ce9adb7738db167b55c877d7f47bbd644f1bb832df38c4213651243fcbe668
7
+ data.tar.gz: 9ed84b118c29ec8531407f4b069840d1c576eb6f120f58ead3a8560242af44b62ca3b6339f4949a5433c64fc966003d65f2871134b0ed17984cc2f461b9537bd
@@ -0,0 +1,4 @@
1
+ .git
2
+ pkg
3
+ docs
4
+ spec
@@ -1,8 +1,25 @@
1
- # Change Log
1
+ # Changelog
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [0.3.0]
7
+ - #19 new commands: exec, logs
8
+ - delete preview
9
+ - show pods as part of get
10
+
11
+ ## [0.2.6]
12
+ - #18 gcloud builder. change to config.builder
13
+
14
+ ## [0.2.5]
15
+ - #17 cloudbuild build strategy
16
+
17
+ ## [0.2.4]
18
+ - #16 google cloudbuild support & docs: add Dockerfile for kubes as a docker entrypoint
19
+
20
+ ## [0.2.3]
21
+ - #15 use kubernetes default deployment strategy instead
22
+
6
23
  ## [0.2.2]
7
24
  - #14 init template updates, dockerfile_port helper
8
25
 
@@ -0,0 +1,14 @@
1
+ FROM ruby:2.7-alpine
2
+
3
+ RUN apk add --no-cache docker
4
+ RUN apk add --no-cache build-base ruby ruby-dev
5
+
6
+ RUN wget https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl
7
+ RUN chmod u+x kubectl && mv kubectl /bin/kubectl
8
+
9
+ WORKDIR /app
10
+ ADD . /app
11
+ RUN bundle install
12
+ RUN rake install
13
+
14
+ ENTRYPOINT ["/usr/local/bundle/bin/kubes"]
@@ -22,7 +22,7 @@ You can override configs on a per-env basis with `config/env` files. Examples:
22
22
  ```ruby
23
23
  Kubes.configure do |config|
24
24
  config.repo = "222222222222.dkr.ecr.us-west-2.amazonaws.com/demo"
25
- config.kubectl.context = "dev-services"
25
+ config.kubectl.context = "dev-cluster"
26
26
  end
27
27
  ```
28
28
 
@@ -31,7 +31,7 @@ end
31
31
  ```ruby
32
32
  Kubes.configure do |config|
33
33
  config.repo = "333333333333.dkr.ecr.us-west-2.amazonaws.com/demo"
34
- config.kubectl.context = "prod-services"
34
+ config.kubectl.context = "prod-cluster"
35
35
  end
36
36
  ```
37
37
 
@@ -39,8 +39,8 @@ end
39
39
 
40
40
  With this setup, when you deploy with kubes, it will automatically switch the kubectl context based on `KUBES_ENV`. Example:
41
41
 
42
- KUBES_ENV=dev kubes deploy # to dev-services context
43
- KUBES_ENV=prod kubes deploy # to prod-services context
42
+ KUBES_ENV=dev kubes deploy # to dev-cluster context
43
+ KUBES_ENV=prod kubes deploy # to prod-cluster context
44
44
 
45
45
  ## context_keep Option
46
46
 
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: CloudBuild
3
+ ---
4
+
5
+ To build kubes as a Docker image entrypoint for [Google CloudBuild Custom Builder](https://cloud.google.com/cloud-build/docs/configuring-builds/use-community-and-custom-builders).
6
+
7
+ git clone http://github.com/boltops-tools/kubes
8
+ cd kubes
9
+ gcloud builds submit --tag gcr.io/$GOOGLE_PROJECT/kubes
10
+
11
+ Be sure to set GOOGLE_PROJECT to your own project id.
12
+
13
+ ## Example Codebuild YAML
14
+
15
+ cloudbuild.yaml:
16
+
17
+ ```yaml
18
+ steps:
19
+ # Simply calling kubectl version with the CLOUDSDK_* vars will auth to the GKE cluster. Unsure why.
20
+ - name: gcr.io/cloud-builders/kubectl
21
+ args: ['version']
22
+ env:
23
+ - 'CLOUDSDK_COMPUTE_REGION=$_GCP_REGION'
24
+ - 'CLOUDSDK_CONTAINER_CLUSTER=$_GKE_CLUSTER'
25
+ - name: 'gcr.io/$PROJECT_ID/kubes'
26
+ args: ["deploy"]
27
+ env:
28
+ - 'GOOGLE_PROJECT=$PROJECT_ID' # .kubes/config.rb: config.repo
29
+ - 'KUBES_ENV=$_KUBES_ENV'
30
+ - 'KUBES_EXTRA=$_KUBES_EXTRA'
31
+
32
+ substitutions:
33
+ _GCP_REGION: us-central1
34
+ _GKE_CLUSTER: dev-cluster
35
+ _KUBES_ENV: dev
36
+ _KUBES_EXTRA: ''
37
+ options:
38
+ substitution_option: 'ALLOW_LOOSE'
39
+ ```
40
+
41
+ ## Run CloudBuild
42
+
43
+ Run cloudbuild with:
44
+
45
+ gcloud builds submit --config cloudbuild.yaml
46
+
47
+ Example with output:
48
+
49
+ $ gcloud builds submit --config cloudbuild.yaml
50
+ Starting Step #1
51
+ Step #1: => docker build -t gcr.io/tung-275700/demo:kubes-2020-07-25T21-13-59 -f Dockerfile .
52
+ Step #1: Pushed gcr.io/tung-275700/demo:kubes-2020-07-25T21-13-59 docker image.
53
+ Step #1: Docker push took 2s.
54
+ Step #1: Compiled .kubes/resources files to .kubes/output
55
+ Step #1: Deploying kubes resources
56
+ Step #1: => kubectl apply -f .kubes/output/shared/namespace.yaml
57
+ Step #1: namespace/demo unchanged
58
+ Step #1: => kubectl apply -f .kubes/output/web/service.yaml
59
+ Step #1: service/web unchanged
60
+ Step #1: => kubectl apply -f .kubes/output/web/deployment.yaml
61
+ Step #1: deployment.apps/web configured
62
+ $
63
+
64
+ ## Create Extra Environments
65
+
66
+ If you are using the [with_extra helper]({% link _docs/extra-env.md %}), you can create additional environments of the same app like so:
67
+
68
+ gcloud builds submit --config cloudbuild.yaml --substitutions=_KUBES_EXTRA=2
69
+ gcloud builds submit --config cloudbuild.yaml --substitutions=_KUBES_EXTRA=3
@@ -0,0 +1,46 @@
1
+ ---
2
+ title: Builder Strategy
3
+ ---
4
+
5
+ Kubes uses the `docker` command to build the docker image by default. Kubes can also support an additional builder: gcloud.
6
+
7
+ ## Gcloud Builder
8
+
9
+ With the gcloud builder, you do not need docker installed locally. Google CloudBuild is used to build and push the image to a GCR registry.
10
+
11
+ You must set up the [gcloud cli](https://cloud.google.com/sdk/gcloud/reference/builds/submit). Please refer to the [gcloud sdk install docs](https://cloud.google.com/sdk/install). Kubes will call out to `gcloud builds submit` to create the Docker image.
12
+
13
+ ## Configure
14
+
15
+ You configure gcloud as the builder like so:
16
+
17
+ ```ruby
18
+ Kubes.configure do |config|
19
+ config.repo = "gcr.io/#{ENV['GOOGLE_PROJECT']}/demo"
20
+ config.logger.level = "info"
21
+ config.builder = "gcloud" # <= changed to gcloud
22
+ end
23
+ ```
24
+
25
+ ## Commands
26
+
27
+ The kubes builds command will remain the same.
28
+
29
+ kubes docker build
30
+
31
+ There is no need to run the push command, as the build command with the gcloud builder will always push.
32
+
33
+ ## Deploy
34
+
35
+ The deploy commands remain the same. Example:
36
+
37
+ kubes deploy web
38
+
39
+ If you want to skip the `docker build` phase of the deploy, you can run:
40
+
41
+ kubes deploy web --no-build
42
+
43
+ Also, kubes apply another way to skip the docker build:
44
+
45
+ kubes apply web
46
+
@@ -11,7 +11,7 @@ Kubes.configure do |config|
11
11
  config.repo = "111111111111.dkr.ecr.us-west-2.amazonaws.com/demo"
12
12
  config.logger.level = "info"
13
13
  # auto-switching
14
- # config.kubectl.context = "dev-services"
14
+ # config.kubectl.context = "dev-cluster"
15
15
  # config.kubectl.context_keep = false
16
16
  end
17
17
  ```
@@ -25,7 +25,7 @@ You can override configs on a per-env basis with `config/env` files. Examples:
25
25
  ```ruby
26
26
  Kubes.configure do |config|
27
27
  config.repo = "222222222222.dkr.ecr.us-west-2.amazonaws.com/demo"
28
- config.kubectl.context = "dev-services"
28
+ config.kubectl.context = "dev-cluster"
29
29
  end
30
30
  ```
31
31
 
@@ -34,7 +34,7 @@ end
34
34
  ```ruby
35
35
  Kubes.configure do |config|
36
36
  config.repo = "333333333333.dkr.ecr.us-west-2.amazonaws.com/demo"
37
- config.kubectl.context = "prod-services"
37
+ config.kubectl.context = "prod-cluster"
38
38
  end
39
39
  ```
40
40
 
@@ -34,11 +34,6 @@ spec:
34
34
  selector:
35
35
  matchLabels:
36
36
  app: demo
37
- strategy:
38
- rollingUpdate:
39
- maxSurge: 25
40
- maxUnavailable: 25
41
- type: RollingUpdate
42
37
  template:
43
38
  metadata:
44
39
  labels:
@@ -50,7 +50,7 @@ Note, the behavior of the from is an *or* since the namespaceSelector and podSel
50
50
 
51
51
  ## Example 2
52
52
 
53
- If you need more control over the ingress selectors you can use the from method. He's an example:
53
+ If you need more control over the ingress selectors, you can use the `from` method. Here's an example:
54
54
 
55
55
  .kubes/resources/web/network_policy.rb
56
56
 
@@ -46,15 +46,31 @@ spec:
46
46
 
47
47
  ## DSL Methods
48
48
 
49
- Here's a list of more common methods:
49
+ Here's a list of some of the methods:
50
+
51
+ kubectl explain service.spec
52
+
53
+ * clusterIP
54
+ * externalIPs
55
+ * externalName
56
+ * externalTrafficPolicy
57
+ * healthCheckNodePort
58
+ * ipFamily
59
+ * loadBalancerIP
60
+ * loadBalancerSourceRanges
61
+ * ports
62
+ * publishNotReadyAddresses
63
+ * selector
64
+ * sessionAffinity
65
+ * sessionAffinityConfig
66
+ * type
67
+
68
+ kubectl explain service.spec.ports
50
69
 
51
70
  * nodePort
52
71
  * port
53
- * portName: Note this field doesn't match the original field name. It's more qualified.
54
- * ports
72
+ * portName: : Note this field doesn't match the original field name. It's more qualified.
55
73
  * protocol
56
- * selector
57
74
  * targetPort
58
- * type
59
75
 
60
76
  {% include dsl/methods.md name="service" %}
@@ -8,8 +8,8 @@ Helper | Description
8
8
  --- | ---
9
9
  built_image | Method refers to the latest Docker image built by Kubes. This spares you from having to update the image manually in the deployment resource.
10
10
  dockerfile_port | Exposed port extracted from the Dockerfile of the project.
11
- extra | The `KUBES_ENV` value.
12
- with_extra | Appends the `KUBES_ENV` value to a string if it's set. It's covered in the [Extra Env Docs]({% link _docs/extra-env.md %}).
11
+ extra | The `KUBES_EXTRA` value.
12
+ with_extra | Appends the `KUBES_EXTRA` value to a string if it's set. It's covered in the [Extra Env Docs]({% link _docs/extra-env.md %}).
13
13
 
14
14
  Here's also the source code with the helpers: [helpers.rb](https://github.com/boltops-tools/kubes/blob/master/lib/kubes/compiler/shared/helpers.rb).
15
15
 
@@ -22,6 +22,8 @@ You'll see output like this:
22
22
  deployment.apps/demo-web created
23
23
  $
24
24
 
25
+ Note: Showing an AWS ECR repo, but it may be different if you're using another repo like Google GCR.
26
+
25
27
  What did Kubes do?
26
28
 
27
29
  {% include kubes-steps.md %}
@@ -7,11 +7,10 @@ If you already a project with an existing Dockerfile, you can use that. If you d
7
7
  mkdir demo
8
8
  cd demo
9
9
 
10
- For this tutorial, we'll use an ECR repo, though any repo will work.
10
+ {% include learn/repos.md %}
11
11
 
12
12
  Let's generate a starter project:
13
13
 
14
- $ REPO=$(aws ecr describe-repositories --repository-name demo | jq -r '.repositories[].repositoryUri')
15
14
  $ kubes init --app demo --repo $REPO --type dsl
16
15
  create .kubes/config.rb
17
16
  create .kubes/config/env/dev.rb
@@ -22,6 +22,8 @@ You'll see output like this:
22
22
  deployment.apps/demo-web created
23
23
  $
24
24
 
25
+ Note: Showing an AWS ECR repo, but it may be different if you're using another repo like Google GCR.
26
+
25
27
  What did Kubes do?
26
28
 
27
29
  {% include kubes-steps.md %}
@@ -7,7 +7,7 @@ If you already a project with an existing Dockerfile, you can use that. If you d
7
7
  mkdir demo
8
8
  cd demo
9
9
 
10
- For this tutorial, we'll use an ECR repo, though any repo will work.
10
+ {% include learn/repos.md %}
11
11
 
12
12
  Let's generate a starter project:
13
13
 
@@ -37,11 +37,6 @@ spec:
37
37
  selector:
38
38
  matchLabels:
39
39
  role: web
40
- strategy:
41
- rollingUpdate:
42
- maxSurge: 25
43
- maxUnavailable: 25
44
- type: RollingUpdate
45
40
  template:
46
41
  metadata:
47
42
  labels:
@@ -30,11 +30,6 @@ spec:
30
30
  selector:
31
31
  matchLabels:
32
32
  app: demo
33
- strategy:
34
- rollingUpdate:
35
- maxSurge: 25
36
- maxUnavailable: 25
37
- type: RollingUpdate
38
33
  template:
39
34
  metadata:
40
35
  labels:
@@ -4,7 +4,7 @@ Install kubes via RubyGems.
4
4
 
5
5
  gem install kubes
6
6
 
7
- Ruby 2.7 and patch variants of it is recommended.
7
+ Ruby 2.7 and above is recommended.
8
8
 
9
9
  ## kubectl
10
10
 
@@ -13,3 +13,7 @@ Kubes calls kubectl. Kubes has been tested with kubectl v1.15+. Generally, it sh
13
13
  ## docker
14
14
 
15
15
  Kubes calls docker. Kubes has been tested with docker 18.x-ce+. Generally, it should work with most versions of docker.
16
+
17
+ ## gcloud
18
+
19
+ If you are using the [gcloud builder](% link _docs/config/builder.md %), set up and configure the [gcloud cli](https://cloud.google.com/sdk/install).
@@ -1,12 +1,12 @@
1
- You'll need a Kubernetes cluster and a Docker repo to be able to push to. Setting up a Kubernetes cluster is a little bit out scope for this tutorial. We'll provide the docs and links though. We'll also use an AWS EKS cluster and ECR.
1
+ You'll need a Kubernetes cluster and a Docker repo to be able to push to. Setting up a Kubernetes cluster is a little bit out scope for this tutorial. We'll provide the docs and links though. We'll provide instructions for AWS and Google.
2
2
 
3
- ## Create an EKS cluster
3
+ ## AWS: Create an EKS cluster
4
4
 
5
5
  Here are the AWS docs to create an EKS cluster:
6
6
 
7
7
  * [Creating an Amazon EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html)
8
8
 
9
- ## Create an ECR Repo
9
+ ## AWS: Create an ECR Repo
10
10
 
11
11
  Here are the AWS docs to create a repo:
12
12
 
@@ -19,4 +19,21 @@ Here are also the commands to create an ECR repo:
19
19
 
20
20
  We'll be using the `$REPO` variable for the rest of the tutorial.
21
21
 
22
- Note: Though we're using AWS here, Kubes works with any Cloud Provider. Please adjust your commands and the `$REPO` variable for your cloud provider.
22
+ ## Google: Create GKE Cluster
23
+
24
+ Here are the Google docs to create an GKE cluster:
25
+
26
+ * [Creating an Google GKE cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-regional-cluster)
27
+
28
+ ## Google: GCR Repo
29
+
30
+ For google, you do not have to create a repo. You simply push to the right repo url. Example:
31
+
32
+ export GOOGLE_PROJECT=project-123
33
+ REPO=gcr.io/$GOOGLE_PROJECT/demo
34
+
35
+ ## Repo Variable
36
+
37
+ We'll be using the `$REPO` variable for the rest of the tutorial.
38
+
39
+ Note: Though we're using AWS and Google here, Kubes works with any Cloud Provider. Please adjust your commands and the `$REPO` variable for your cloud provider.
File without changes
@@ -0,0 +1,10 @@
1
+ For this tutorial, here are examples of AWS ECR and Google GCR repos, though any repo will work.
2
+
3
+ AWS:
4
+
5
+ REPO=$(aws ecr describe-repositories --repository-name demo | jq -r '.repositories[].repositoryUri')
6
+
7
+ Google:
8
+
9
+ export GOOGLE_PROJECT=project-123
10
+ REPO=gcr.io/$GOOGLE_PROJECT/demo
@@ -4,10 +4,10 @@ The `config.rb` is where you can configure Kubes settings.
4
4
 
5
5
  ```ruby
6
6
  Kubes.configure do |config|
7
- config.repo = "112233445566.dkr.ecr.us-west-2.amazonaws.com/demo"
7
+ config.repo = "112233445566.dkr.ecr.us-west-2.amazonaws.com/demo" # may be gcr.io/project-123/demo
8
8
  config.logger.level = "info"
9
9
  # auto-switching
10
- # config.kubectl.context = "dev-services"
10
+ # config.kubectl.context = "dev-cluster"
11
11
  end
12
12
  ```
13
13