kubeclient 2.5.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kubeclient might be problematic. Click here for more details.

Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -5
  3. data/.travis.yml +2 -10
  4. data/CHANGELOG.md +21 -2
  5. data/README.md +112 -46
  6. data/Rakefile +2 -5
  7. data/kubeclient.gemspec +9 -7
  8. data/lib/kubeclient.rb +8 -8
  9. data/lib/kubeclient/common.rb +88 -105
  10. data/lib/kubeclient/config.rb +10 -10
  11. data/lib/kubeclient/http_error.rb +25 -0
  12. data/lib/kubeclient/missing_kind_compatibility.rb +1 -1
  13. data/lib/kubeclient/resource.rb +11 -0
  14. data/lib/kubeclient/resource_not_found_error.rb +4 -0
  15. data/lib/kubeclient/version.rb +1 -1
  16. data/lib/kubeclient/watch_stream.rb +17 -10
  17. data/test/test_common.rb +3 -3
  18. data/test/test_component_status.rb +16 -14
  19. data/test/test_config.rb +11 -11
  20. data/test/test_endpoint.rb +16 -12
  21. data/test/test_guestbook_go.rb +64 -62
  22. data/test/test_helper.rb +2 -0
  23. data/test/test_kubeclient.rb +268 -282
  24. data/test/test_limit_range.rb +11 -11
  25. data/test/test_missing_methods.rb +3 -3
  26. data/test/test_namespace.rb +27 -27
  27. data/test/test_node.rb +17 -15
  28. data/test/test_persistent_volume.rb +16 -14
  29. data/test/test_persistent_volume_claim.rb +16 -14
  30. data/test/test_pod.rb +16 -14
  31. data/test/test_pod_log.rb +4 -4
  32. data/test/test_process_template.rb +7 -7
  33. data/test/test_replication_controller.rb +29 -4
  34. data/test/test_resource_list_without_kind.rb +7 -7
  35. data/test/test_resource_quota.rb +4 -4
  36. data/test/test_secret.rb +11 -10
  37. data/test/test_service.rb +36 -31
  38. data/test/test_service_account.rb +4 -4
  39. data/test/test_watch.rb +52 -29
  40. data/test/test_watch_notice.rb +1 -1
  41. metadata +35 -26
  42. data/Gemfile-rest-client-1.8.rb +0 -11
  43. data/lib/kubeclient/kube_exception.rb +0 -14
@@ -1,27 +1,27 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # LimitRange tests
4
4
  class TestLimitRange < MiniTest::Test
5
5
  def test_get_from_json_v1
6
6
  stub_request(:get, %r{/api/v1$})
7
- .to_return(body: open_test_file('core_api_resource_list.json'),
8
- status: 200)
7
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
9
8
 
10
9
  stub_request(:get, %r{/limitranges})
11
- .to_return(body: open_test_file('limit_range.json'),
12
- status: 200)
10
+ .to_return(body: open_test_file('limit_range.json'), status: 200)
13
11
 
14
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
15
- limit_range = client.get_limit_range 'limits', 'quota-example'
12
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
13
+ limit_range = client.get_limit_range('limits', 'quota-example')
16
14
 
17
- assert_instance_of(Kubeclient::LimitRange, limit_range)
15
+ assert_instance_of(Kubeclient::Resource, limit_range)
18
16
  assert_equal('limits', limit_range.metadata.name)
19
17
  assert_equal('Container', limit_range.spec.limits[0].type)
20
18
  assert_equal('100m', limit_range.spec.limits[0].default.cpu)
21
19
  assert_equal('512Mi', limit_range.spec.limits[0].default.memory)
22
20
 
23
- assert_requested(:get,
24
- 'http://localhost:8080/api/v1/namespaces/quota-example/limitranges/limits',
25
- times: 1)
21
+ assert_requested(
22
+ :get,
23
+ 'http://localhost:8080/api/v1/namespaces/quota-example/limitranges/limits',
24
+ times: 1
25
+ )
26
26
  end
27
27
  end
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Test method_missing, respond_to? and respond_to_missing behaviour
4
4
  class TestMissingMethods < MiniTest::Test
@@ -31,11 +31,11 @@ class TestMissingMethods < MiniTest::Test
31
31
  status: 404
32
32
  ) # If discovery fails we expect the below raise an exception
33
33
  client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
34
- assert_raises(KubeException) do
34
+ assert_raises(Kubeclient::HttpError) do
35
35
  client.method(:get_pods)
36
36
  end
37
37
  client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
38
- assert_raises(KubeException) do
38
+ assert_raises(Kubeclient::HttpError) do
39
39
  client.respond_to?(:get_pods)
40
40
  end
41
41
  end
@@ -1,27 +1,27 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Namespace entity tests
4
4
  class TestNamespace < MiniTest::Test
5
5
  def test_get_namespace_v1
6
6
  stub_request(:get, %r{/api/v1$})
7
- .to_return(body: open_test_file('core_api_resource_list.json'),
8
- status: 200)
7
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
9
8
  stub_request(:get, %r{/namespaces})
10
- .to_return(body: open_test_file('namespace.json'),
11
- status: 200)
9
+ .to_return(body: open_test_file('namespace.json'), status: 200)
12
10
 
13
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
14
- namespace = client.get_namespace 'staging'
11
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
12
+ namespace = client.get_namespace('staging')
15
13
 
16
- assert_instance_of(Kubeclient::Namespace, namespace)
14
+ assert_instance_of(Kubeclient::Resource, namespace)
17
15
  assert_equal('e388bc10-c021-11e4-a514-3c970e4a436a', namespace.metadata.uid)
18
16
  assert_equal('staging', namespace.metadata.name)
19
17
  assert_equal('1168', namespace.metadata.resourceVersion)
20
18
  assert_equal('v1', namespace.apiVersion)
21
19
 
22
- assert_requested(:get,
23
- 'http://localhost:8080/api/v1/namespaces/staging',
24
- times: 1)
20
+ assert_requested(
21
+ :get,
22
+ 'http://localhost:8080/api/v1/namespaces/staging',
23
+ times: 1
24
+ )
25
25
  end
26
26
 
27
27
  def test_delete_namespace_v1
@@ -30,33 +30,33 @@ class TestNamespace < MiniTest::Test
30
30
  our_namespace.metadata.name = 'staging'
31
31
 
32
32
  stub_request(:get, %r{/api/v1$})
33
- .to_return(body: open_test_file('core_api_resource_list.json'),
34
- status: 200)
33
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
35
34
  stub_request(:delete, %r{/namespaces})
36
- .to_return(status: 200)
37
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
38
- client.delete_namespace our_namespace.metadata.name
39
-
40
- assert_requested(:delete,
41
- 'http://localhost:8080/api/v1/namespaces/staging',
42
- times: 1)
35
+ .to_return(body: open_test_file('namespace.json'), status: 200)
36
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
37
+ our_namespace = client.delete_namespace(our_namespace.metadata.name)
38
+ assert_kind_of(RecursiveOpenStruct, our_namespace)
39
+
40
+ assert_requested(
41
+ :delete,
42
+ 'http://localhost:8080/api/v1/namespaces/staging',
43
+ times: 1
44
+ )
43
45
  end
44
46
 
45
47
  def test_create_namespace
46
48
  stub_request(:get, %r{/api/v1$})
47
- .to_return(body: open_test_file('core_api_resource_list.json'),
48
- status: 200)
49
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
49
50
  stub_request(:post, %r{/namespaces})
50
- .to_return(body: open_test_file('created_namespace.json'),
51
- status: 201)
51
+ .to_return(body: open_test_file('created_namespace.json'), status: 201)
52
52
 
53
53
  namespace = Kubeclient::Resource.new
54
54
  namespace.metadata = {}
55
55
  namespace.metadata.name = 'development'
56
56
 
57
- client = Kubeclient::Client.new 'http://localhost:8080/api/'
58
- created_namespace = client.create_namespace namespace
59
- assert_instance_of(Kubeclient::Namespace, created_namespace)
57
+ client = Kubeclient::Client.new('http://localhost:8080/api/')
58
+ created_namespace = client.create_namespace(namespace)
59
+ assert_instance_of(Kubeclient::Resource, created_namespace)
60
60
  assert_equal(namespace.metadata.name, created_namespace.metadata.name)
61
61
  end
62
62
  end
@@ -1,19 +1,17 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Node entity tests
4
4
  class TestNode < MiniTest::Test
5
5
  def test_get_from_json_v1
6
6
  stub_request(:get, %r{/nodes})
7
- .to_return(body: open_test_file('node.json'),
8
- status: 200)
7
+ .to_return(body: open_test_file('node.json'), status: 200)
9
8
  stub_request(:get, %r{/api/v1$})
10
- .to_return(body: open_test_file('core_api_resource_list.json'),
11
- status: 200)
9
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
12
10
 
13
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
11
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
14
12
  node = client.get_node('127.0.0.1')
15
13
 
16
- assert_instance_of(Kubeclient::Node, node)
14
+ assert_instance_of(Kubeclient::Resource, node)
17
15
 
18
16
  assert_equal('041143c5-ce39-11e4-ac24-3c970e4a436a', node.metadata.uid)
19
17
  assert_equal('127.0.0.1', node.metadata.name)
@@ -21,12 +19,16 @@ class TestNode < MiniTest::Test
21
19
  assert_equal('v1', node.apiVersion)
22
20
  assert_equal('2015-03-19T15:08:20+02:00', node.metadata.creationTimestamp)
23
21
 
24
- assert_requested(:get,
25
- 'http://localhost:8080/api/v1',
26
- times: 1)
27
- assert_requested(:get,
28
- 'http://localhost:8080/api/v1/nodes/127.0.0.1',
29
- times: 1)
22
+ assert_requested(
23
+ :get,
24
+ 'http://localhost:8080/api/v1',
25
+ times: 1
26
+ )
27
+ assert_requested(
28
+ :get,
29
+ 'http://localhost:8080/api/v1/nodes/127.0.0.1',
30
+ times: 1
31
+ )
30
32
  end
31
33
 
32
34
  def test_get_from_json_v1_raw
@@ -60,11 +62,11 @@ class TestNode < MiniTest::Test
60
62
 
61
63
  client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
62
64
 
63
- exception = assert_raises(KubeException) do
65
+ exception = assert_raises(Kubeclient::HttpError) do
64
66
  client.get_node('127.0.0.1', nil, as: :raw)
65
67
  end
66
68
 
67
- assert_instance_of(KubeException, exception)
69
+ assert_instance_of(Kubeclient::HttpError, exception)
68
70
  assert_equal('500 Internal Server Error', exception.message)
69
71
  end
70
72
  end
@@ -1,28 +1,30 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # PersistentVolume tests
4
4
  class TestPersistentVolume < MiniTest::Test
5
5
  def test_get_from_json_v1
6
6
  stub_request(:get, %r{/persistentvolumes})
7
- .to_return(body: open_test_file('persistent_volume.json'),
8
- status: 200)
7
+ .to_return(body: open_test_file('persistent_volume.json'), status: 200)
9
8
  stub_request(:get, %r{/api/v1$})
10
- .to_return(body: open_test_file('core_api_resource_list.json'),
11
- status: 200)
9
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
12
10
 
13
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
14
- volume = client.get_persistent_volume 'pv0001'
11
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
12
+ volume = client.get_persistent_volume('pv0001')
15
13
 
16
- assert_instance_of(Kubeclient::PersistentVolume, volume)
14
+ assert_instance_of(Kubeclient::Resource, volume)
17
15
  assert_equal('pv0001', volume.metadata.name)
18
16
  assert_equal('10Gi', volume.spec.capacity.storage)
19
17
  assert_equal('/tmp/data01', volume.spec.hostPath.path)
20
18
 
21
- assert_requested(:get,
22
- 'http://localhost:8080/api/v1',
23
- times: 1)
24
- assert_requested(:get,
25
- 'http://localhost:8080/api/v1/persistentvolumes/pv0001',
26
- times: 1)
19
+ assert_requested(
20
+ :get,
21
+ 'http://localhost:8080/api/v1',
22
+ times: 1
23
+ )
24
+ assert_requested(
25
+ :get,
26
+ 'http://localhost:8080/api/v1/persistentvolumes/pv0001',
27
+ times: 1
28
+ )
27
29
  end
28
30
  end
@@ -1,28 +1,30 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # PersistentVolumeClaim tests
4
4
  class TestPersistentVolumeClaim < MiniTest::Test
5
5
  def test_get_from_json_v1
6
6
  stub_request(:get, %r{/persistentvolumeclaims})
7
- .to_return(body: open_test_file('persistent_volume_claim.json'),
8
- status: 200)
7
+ .to_return(body: open_test_file('persistent_volume_claim.json'), status: 200)
9
8
  stub_request(:get, %r{/api/v1$})
10
- .to_return(body: open_test_file('core_api_resource_list.json'),
11
- status: 200)
9
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
12
10
 
13
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
14
- claim = client.get_persistent_volume_claim 'myclaim-1', 'default'
11
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
12
+ claim = client.get_persistent_volume_claim('myclaim-1', 'default')
15
13
 
16
- assert_instance_of(Kubeclient::PersistentVolumeClaim, claim)
14
+ assert_instance_of(Kubeclient::Resource, claim)
17
15
  assert_equal('myclaim-1', claim.metadata.name)
18
16
  assert_equal('3Gi', claim.spec.resources.requests.storage)
19
17
  assert_equal('pv0001', claim.spec.volumeName)
20
18
 
21
- assert_requested(:get,
22
- 'http://localhost:8080/api/v1',
23
- times: 1)
24
- assert_requested(:get,
25
- 'http://localhost:8080/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1',
26
- times: 1)
19
+ assert_requested(
20
+ :get,
21
+ 'http://localhost:8080/api/v1',
22
+ times: 1
23
+ )
24
+ assert_requested(
25
+ :get,
26
+ 'http://localhost:8080/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1',
27
+ times: 1
28
+ )
27
29
  end
28
30
  end
@@ -1,27 +1,29 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Pod entity tests
4
4
  class TestPod < MiniTest::Test
5
5
  def test_get_from_json_v1
6
6
  stub_request(:get, %r{/pods})
7
- .to_return(body: open_test_file('pod.json'),
8
- status: 200)
7
+ .to_return(body: open_test_file('pod.json'), status: 200)
9
8
  stub_request(:get, %r{/api/v1$})
10
- .to_return(body: open_test_file('core_api_resource_list.json'),
11
- status: 200)
9
+ .to_return(body: open_test_file('core_api_resource_list.json'), status: 200)
12
10
 
13
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
14
- pod = client.get_pod 'redis-master-pod', 'default'
11
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
12
+ pod = client.get_pod('redis-master-pod', 'default')
15
13
 
16
- assert_instance_of(Kubeclient::Pod, pod)
14
+ assert_instance_of(Kubeclient::Resource, pod)
17
15
  assert_equal('redis-master3', pod.metadata.name)
18
16
  assert_equal('dockerfile/redis', pod.spec.containers[0]['image'])
19
17
 
20
- assert_requested(:get,
21
- 'http://localhost:8080/api/v1',
22
- times: 1)
23
- assert_requested(:get,
24
- 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod',
25
- times: 1)
18
+ assert_requested(
19
+ :get,
20
+ 'http://localhost:8080/api/v1',
21
+ times: 1
22
+ )
23
+ assert_requested(
24
+ :get,
25
+ 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod',
26
+ times: 1
27
+ )
26
28
  end
27
29
  end
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Pod log tests
4
4
  class TestPodLog < MiniTest::Test
@@ -7,7 +7,7 @@ class TestPodLog < MiniTest::Test
7
7
  .to_return(body: open_test_file('pod_log.txt'),
8
8
  status: 200)
9
9
 
10
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
10
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
11
11
  retrieved_log = client.get_pod_log('redis-master-pod', 'default')
12
12
 
13
13
  assert_equal(open_test_file('pod_log.txt').read, retrieved_log)
@@ -22,7 +22,7 @@ class TestPodLog < MiniTest::Test
22
22
  .to_return(body: open_test_file('pod_log.txt'),
23
23
  status: 200)
24
24
 
25
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
25
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
26
26
  retrieved_log = client.get_pod_log('redis-master-pod', 'default', container: 'ruby')
27
27
 
28
28
  assert_equal(open_test_file('pod_log.txt').read, retrieved_log)
@@ -39,7 +39,7 @@ class TestPodLog < MiniTest::Test
39
39
  .to_return(body: open_test_file('pod_log.txt'),
40
40
  status: 200)
41
41
 
42
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
42
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
43
43
 
44
44
  stream = client.watch_pod_log('redis-master-pod', 'default')
45
45
  stream.to_enum.with_index do |notice, index|
@@ -1,9 +1,9 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Process Template tests
4
4
  class TestProcessTemplate < MiniTest::Test
5
5
  def test_process_template
6
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
6
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
7
7
  template = {}
8
8
  template[:metadata] = {}
9
9
  template[:metadata][:name] = 'my-template'
@@ -19,17 +19,17 @@ class TestProcessTemplate < MiniTest::Test
19
19
  param = { name: 'NAME_PREFIX', value: 'test/' }
20
20
  template[:parameters] = [param]
21
21
 
22
- req_body = "{\"metadata\":{\"name\":\"my-template\",\"namespace\":\"default\"},"\
23
- "\"kind\":\"Template\",\"apiVersion\":\"v1\",\"objects\":[{\"metadata\":"\
24
- "{\"name\":\"${NAME_PREFIX}my-service\"},\"kind\":\"Service\",\"apiVersion\":\"v1\"}],"\
25
- "\"parameters\":[{\"name\":\"NAME_PREFIX\",\"value\":\"test/\"}]}"
22
+ req_body = '{"metadata":{"name":"my-template","namespace":"default"},' \
23
+ '"kind":"Template","apiVersion":"v1","objects":[{"metadata":' \
24
+ '{"name":"${NAME_PREFIX}my-service"},"kind":"Service","apiVersion":"v1"}],' \
25
+ '"parameters":[{"name":"NAME_PREFIX","value":"test/"}]}'
26
26
 
27
27
  expected_url = 'http://localhost:8080/api/v1/namespaces/default/processedtemplates'
28
28
  stub_request(:post, expected_url)
29
29
  .with(body: req_body, headers: { 'Content-Type' => 'application/json' })
30
30
  .to_return(body: open_test_file('processed_template.json'), status: 200)
31
31
 
32
- processed_template = client.process_template template
32
+ processed_template = client.process_template(template)
33
33
 
34
34
  assert_equal('test/my-service', processed_template['objects'].first['metadata']['name'])
35
35
 
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  # Replication Controller entity tests
4
4
  class TestReplicationController < MiniTest::Test
@@ -10,10 +10,10 @@ class TestReplicationController < MiniTest::Test
10
10
  .to_return(body: open_test_file('replication_controller.json'),
11
11
  status: 200)
12
12
 
13
- client = Kubeclient::Client.new 'http://localhost:8080/api/', 'v1'
14
- rc = client.get_replication_controller 'frontendController', 'default'
13
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
14
+ rc = client.get_replication_controller('frontendController', 'default')
15
15
 
16
- assert_instance_of(Kubeclient::ReplicationController, rc)
16
+ assert_instance_of(Kubeclient::Resource, rc)
17
17
  assert_equal('guestbook-controller', rc.metadata.name)
18
18
  assert_equal('c71aa4c0-a240-11e4-a265-3c970e4a436a', rc.metadata.uid)
19
19
  assert_equal('default', rc.metadata.namespace)
@@ -24,4 +24,29 @@ class TestReplicationController < MiniTest::Test
24
24
  'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController',
25
25
  times: 1)
26
26
  end
27
+
28
+ def test_delete_replicaset_cascade
29
+ stub_request(:get, %r{/api/v1$})
30
+ .to_return(body: open_test_file('core_api_resource_list.json'),
31
+ status: 200)
32
+
33
+ client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1')
34
+ opts = Kubeclient::Resource.new(
35
+ apiVersion: 'meta/v1',
36
+ gracePeriodSeconds: 0,
37
+ kind: 'DeleteOptions',
38
+ propagationPolicy: 'Foreground'
39
+ )
40
+
41
+ stub_request(:delete,
42
+ 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController')
43
+ .with(body: opts.to_hash.to_json)
44
+ .to_return(status: 200, body: open_test_file('replication_controller.json'), headers: {})
45
+ rc = client.delete_replication_controller('frontendController', 'default', delete_options: opts)
46
+ assert_kind_of(RecursiveOpenStruct, rc)
47
+
48
+ assert_requested(:delete,
49
+ 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController',
50
+ times: 1)
51
+ end
27
52
  end