dockerapi 0.3.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/setup CHANGED
@@ -1,8 +1,22 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
  IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
4
+ #set -vx
7
5
 
8
6
  # Do any other automated setup that you need to do here
7
+ if [ $USER == 'root' ]
8
+ then
9
+ echo "Enabling TCP port 2375 for external connection to Docker"
10
+ echo '{"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]}' > /etc/docker/daemon.json
11
+ mkdir -p /etc/systemd/system/docker.service.d
12
+ echo '[Service]' > /etc/systemd/system/docker.service.d/override.conf
13
+ echo 'ExecStart=' >> /etc/systemd/system/docker.service.d/override.conf
14
+ echo 'ExecStart=/usr/bin/dockerd' >> /etc/systemd/system/docker.service.d/override.conf
15
+ systemctl daemon-reload
16
+ systemctl restart docker.service
17
+ echo "Done!"
18
+ else
19
+ echo "Running bundle install"
20
+ bundle install
21
+ echo "Run this script as root for further configurations"
22
+ fi
@@ -1,33 +1,35 @@
1
1
  module Docker
2
2
  module API
3
3
  class Base
4
+
5
+ def initialize connection = nil
6
+ raise Docker::API::Error.new("Expect connection to be a Docker::API::Connection class") if connection != nil && !connection.is_a?(Docker::API::Connection)
7
+ @connection = connection || Docker::API::Connection.new
8
+ end
4
9
 
5
10
  private
6
11
 
7
- def self.base_path
12
+ def base_path # TODO: this method to be removed?
8
13
  "/"
9
14
  end
10
15
 
11
- def self.connection
12
- Docker::API::Connection.instance
13
- end
14
-
15
- def self.validate error, permitted_keys, params
16
- not_permitted = params.keys.map(&:to_s) - permitted_keys.map(&:to_s)
17
- raise error if not_permitted.size > 0
16
+ def validate error, permitted, params
17
+ return if params[:skip_validation]
18
+ unpermitted = params.keys.map(&:to_s) - permitted.map(&:to_s)
19
+ raise error.new(permitted, unpermitted) if unpermitted.size > 0
18
20
  end
19
21
 
20
22
  ## Converts Ruby Hash into query parameters
21
23
  ## In general, the format is key=value
22
24
  ## If value is another Hash, it should keep a json syntax {key:value}
23
- def self.hash_to_params h
25
+ def hash_to_params h
24
26
  p = []
25
- h.each { |k,v| p.push( v.is_a?(Hash) ? "#{k}=#{v.to_json}" : "#{k}=#{v}") }
27
+ h.delete_if{ | k, v | k.to_s == "skip_validation" }.each { |k,v| p.push( v.is_a?(Hash) ? "#{k}=#{v.to_json}" : "#{k}=#{v}") }
26
28
  p.join("&").gsub(" ","")
27
29
  end
28
30
 
29
- def self.build_path path, params = {}
30
- p = path.is_a?(Array) ? ([base_path] << path).join("/") : path
31
+ def build_path path, params = {}
32
+ p = path.is_a?(Array) ? ([base_path] << path).join("/") : path # TODO: this line to be removed?
31
33
  params.size > 0 ? [p, hash_to_params(params)].join("?") : p
32
34
  end
33
35
 
@@ -1,26 +1,18 @@
1
- require "excon"
2
- require "singleton"
3
- require "json"
4
1
  module Docker
5
2
  module API
6
3
  class Connection
7
- include Singleton
8
-
9
4
  [:get, :post, :head, :delete, :put].each do | method |
10
5
  define_method(method) { | path | self.request(method: method, path: path) }
11
6
  end
12
7
 
13
- def post(path, body = nil)
14
- self.request(method: :post, path: path, headers: { "Content-Type" => "application/json" }, body: body.to_json)
15
- end
16
-
17
8
  def request params
18
- @connection.request(params)
9
+ Docker::API::Response.new(@connection.request(params).data)
19
10
  end
20
11
 
21
- private
22
- def initialize
23
- @connection = Excon.new('unix:///', :socket => '/var/run/docker.sock')
12
+ def initialize url = nil, params = nil
13
+ url ||= 'unix:///'
14
+ params ||= url == 'unix:///' ? {socket: '/var/run/docker.sock'} : {}
15
+ @connection = Excon.new(url, params)
24
16
  end
25
17
 
26
18
  end
@@ -5,111 +5,111 @@ module Docker
5
5
 
6
6
  class Container < Docker::API::Base
7
7
 
8
- def self.base_path
8
+ def base_path
9
9
  "/containers"
10
10
  end
11
11
 
12
- def self.list params = {}
12
+ def list params = {}
13
13
  validate Docker::API::InvalidParameter, [:all, :limit, :size, :filters], params
14
- connection.get(build_path(["json"], params))
14
+ @connection.get(build_path(["json"], params))
15
15
  end
16
16
 
17
- def self.inspect name, params = {}
17
+ def inspect name, params = {}
18
18
  validate Docker::API::InvalidParameter, [:size], params
19
- connection.get(build_path([name, "json"], params))
19
+ @connection.get(build_path([name, "json"], params))
20
20
  end
21
21
 
22
- def self.top name, params = {}
22
+ def top name, params = {}
23
23
  validate Docker::API::InvalidParameter, [:ps_args], params
24
- connection.get(build_path([name, "top"], params))
24
+ @connection.get(build_path([name, "top"], params))
25
25
  end
26
26
 
27
- def self.changes name
28
- connection.get(build_path([name, "changes"]))
27
+ def changes name
28
+ @connection.get(build_path([name, "changes"]))
29
29
  end
30
30
 
31
- def self.start name, params = {}
31
+ def start name, params = {}
32
32
  validate Docker::API::InvalidParameter, [:detachKeys], params
33
- connection.post(build_path([name, "start"], params))
33
+ @connection.post(build_path([name, "start"], params))
34
34
  end
35
35
 
36
- def self.stop name, params = {}
36
+ def stop name, params = {}
37
37
  validate Docker::API::InvalidParameter, [:t], params
38
- connection.post(build_path([name, "stop"], params))
38
+ @connection.post(build_path([name, "stop"], params))
39
39
  end
40
40
 
41
- def self.restart name, params = {}
41
+ def restart name, params = {}
42
42
  validate Docker::API::InvalidParameter, [:t], params
43
- connection.post(build_path([name, "restart"], params))
43
+ @connection.post(build_path([name, "restart"], params))
44
44
  end
45
45
 
46
- def self.kill name, params = {}
46
+ def kill name, params = {}
47
47
  validate Docker::API::InvalidParameter, [:signal], params
48
- connection.post(build_path([name, "kill"], params))
48
+ @connection.post(build_path([name, "kill"], params))
49
49
  end
50
50
 
51
- def self.wait name, params = {}
51
+ def wait name, params = {}
52
52
  validate Docker::API::InvalidParameter, [:condition], params
53
- connection.post(build_path([name, "wait"], params))
53
+ @connection.post(build_path([name, "wait"], params))
54
54
  end
55
55
 
56
- def self.update name, body = {}
56
+ def update name, body = {}
57
57
  validate Docker::API::InvalidRequestBody, Docker::API::UpdateBody, body
58
- connection.post(build_path([name, "update"]), body)
58
+ @connection.request(method: :post, path: build_path([name, "update"]), headers: {"Content-Type": "application/json"}, body: body.to_json)
59
59
  end
60
60
 
61
- def self.rename name, params = {}
61
+ def rename name, params = {}
62
62
  validate Docker::API::InvalidParameter, [:name], params
63
- connection.post(build_path([name, "rename"], params))
63
+ @connection.post(build_path([name, "rename"], params))
64
64
  end
65
65
 
66
- def self.resize name, params = {}
66
+ def resize name, params = {}
67
67
  validate Docker::API::InvalidParameter, [:w, :h], params
68
- connection.post(build_path([name, "resize"], params))
68
+ @connection.post(build_path([name, "resize"], params))
69
69
  end
70
70
 
71
- def self.prune params = {}
71
+ def prune params = {}
72
72
  validate Docker::API::InvalidParameter, [:filters], params
73
- connection.post(build_path(["prune"], params))
73
+ @connection.post(build_path(["prune"], params))
74
74
  end
75
75
 
76
- def self.pause name
77
- connection.post(build_path([name, "pause"]))
76
+ def pause name
77
+ @connection.post(build_path([name, "pause"]))
78
78
  end
79
79
 
80
- def self.unpause name
81
- connection.post(build_path([name, "unpause"]))
80
+ def unpause name
81
+ @connection.post(build_path([name, "unpause"]))
82
82
  end
83
83
 
84
- def self.remove name, params = {}
84
+ def remove name, params = {}
85
85
  validate Docker::API::InvalidParameter, [:v, :force, :link], params
86
- connection.delete(build_path([name]))
86
+ @connection.delete(build_path([name]))
87
87
  end
88
88
 
89
- def self.logs name, params = {}
89
+ def logs name, params = {}
90
90
  validate Docker::API::InvalidParameter, [:follow, :stdout, :stderr, :since, :until, :timestamps, :tail], params
91
91
 
92
92
  path = build_path([name, "logs"], params)
93
93
 
94
94
  if params[:follow] == true || params[:follow] == 1
95
- connection.request(method: :get, path: path , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
95
+ @connection.request(method: :get, path: path , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
96
96
  else
97
- connection.get(path)
97
+ @connection.get(path)
98
98
  end
99
99
  end
100
100
 
101
- def self.attach name, params = {}
101
+ def attach name, params = {}
102
102
  validate Docker::API::InvalidParameter, [:detachKeys, :logs, :stream, :stdin, :stdout, :stderr], params
103
- connection.request(method: :post, path: build_path([name, "attach"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
103
+ @connection.request(method: :post, path: build_path([name, "attach"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
104
104
  end
105
105
 
106
- def self.create params = {}, body = {}
106
+ def create params = {}, body = {}
107
107
  validate Docker::API::InvalidParameter, [:name], params
108
108
  validate Docker::API::InvalidRequestBody, Docker::API::CreateBody, body
109
- connection.post(build_path(["create"], params), body)
109
+ @connection.request(method: :post, path: build_path(["create"], params), headers: {"Content-Type": "application/json"}, body: body.to_json)
110
110
  end
111
111
 
112
- def self.stats name, params = {}
112
+ def stats name, params = {}
113
113
  validate Docker::API::InvalidParameter, [:stream], params
114
114
  path = build_path([name, "stats"], params)
115
115
 
@@ -117,37 +117,37 @@ module Docker
117
117
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
118
118
  puts chunk
119
119
  end
120
- connection.request(method: :get, path: path , response_block: streamer)
120
+ @connection.request(method: :get, path: path , response_block: streamer)
121
121
  else
122
- connection.get(path)
122
+ @connection.get(path)
123
123
  end
124
124
  end
125
125
 
126
- def self.export name, path = "exported_container"
127
- response = Docker::API::Container.inspect(name)
126
+ def export name, path = "exported_container"
127
+ response = self.inspect(name)
128
128
  if response.status == 200
129
129
  file = File.open(File.expand_path(path), "wb")
130
130
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
131
131
  file.write(chunk)
132
132
  end
133
- response = connection.request(method: :get, path: build_path([name, "export"]) , response_block: streamer)
133
+ response = @connection.request(method: :get, path: build_path([name, "export"]) , response_block: streamer)
134
134
  file.close
135
135
  end
136
136
  response
137
137
  end
138
138
 
139
- def self.archive name, file, params = {}
139
+ def archive name, file, params = {}
140
140
  validate Docker::API::InvalidParameter, [:path, :noOverwriteDirNonDir, :copyUIDGID], params
141
141
 
142
142
  begin # File exists on disk, send it to container
143
143
  file = File.open( File.expand_path( file ) , "r")
144
- response = connection.request(method: :put, path: build_path([name, "archive"], params) , request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
144
+ response = @connection.request(method: :put, path: build_path([name, "archive"], params) , request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
145
145
  file.close
146
146
  rescue Errno::ENOENT # File doesnt exist, get it from container
147
- response = connection.head(build_path([name, "archive"], params))
147
+ response = @connection.head(build_path([name, "archive"], params))
148
148
  if response.status == 200 # file exists in container
149
149
  file = File.open( File.expand_path( file ) , "wb")
150
- response = connection.request(method: :get, path: build_path([name, "archive"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| file.write(chunk) })
150
+ response = @connection.request(method: :get, path: build_path([name, "archive"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| file.write(chunk) })
151
151
  file.close
152
152
  end
153
153
  end
@@ -1,6 +1,17 @@
1
1
  module Docker
2
2
  module API
3
- class InvalidParameter < StandardError; end
4
- class InvalidRequestBody < StandardError; end
3
+ class ValidationError < StandardError
4
+ def initialize permitted, unpermitted
5
+ super("Unpermitted options found: #{unpermitted.to_s}. Permitted are #{permitted.to_s}")
6
+ end
7
+ end
8
+ class Error < StandardError
9
+ def initialize msg = "Error without specific message"
10
+ super(msg)
11
+ end
12
+ end
13
+
14
+ class InvalidParameter < Docker::API::ValidationError; end
15
+ class InvalidRequestBody < Docker::API::ValidationError; end
5
16
  end
6
17
  end
@@ -0,0 +1,39 @@
1
+ module Docker
2
+ module API
3
+ class Exec < Docker::API::Base
4
+
5
+ def base_path
6
+ "/exec"
7
+ end
8
+
9
+ def create name, body = {}
10
+ validate Docker::API::InvalidRequestBody, [:AttachStdin, :AttachStdout, :AttachStderr, :DetachKeys, :Tty, :Env, :Cmd, :Privileged, :User, :WorkingDir], body
11
+ @connection.request(method: :post, path: "/containers/#{name}/exec", headers: {"Content-Type": "application/json"}, body: body.to_json )
12
+ end
13
+
14
+ def start name, body = {}
15
+ validate Docker::API::InvalidRequestBody, [:Detach, :Tty], body
16
+
17
+ stream = ""
18
+ response = @connection.request(method: :post,
19
+ path: "/exec/#{name}/start",
20
+ headers: {"Content-Type": "application/json"},
21
+ body: body.to_json,
22
+ response_block: lambda { |chunk, remaining_bytes, total_bytes| stream += chunk.to_s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?') }
23
+ )
24
+ response.data.merge!({stream: stream})
25
+ response
26
+ end
27
+
28
+ def resize name, params = {}
29
+ validate Docker::API::InvalidParameter, [:w, :h], params
30
+ @connection.post(build_path([name, "resize"], params))
31
+ end
32
+
33
+ def inspect name
34
+ @connection.get(build_path([name, "json"]))
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -7,113 +7,111 @@ module Docker
7
7
  BuildParams = [:dockerfile, :t, :extrahosts, :remote, :q, :nocache, :cachefrom, :pull, :rm, :forcerm, :memory, :memswap, :cpushares, :cpusetcpus, :cpuperiod, :cpuquota, :buildargs, :shmsize, :squash, :labels, :networkmode, :platform, :target, :outputs]
8
8
  class Image < Docker::API::Base
9
9
 
10
- def self.base_path
10
+ def base_path
11
11
  "/images"
12
12
  end
13
13
 
14
- def self.inspect name
15
- connection.get(build_path([name, "json"]))
14
+ def inspect name
15
+ @connection.get(build_path([name, "json"]))
16
16
  end
17
17
 
18
- def self.history name
19
- connection.get(build_path([name, "history"]))
18
+ def history name
19
+ @connection.get(build_path([name, "history"]))
20
20
  end
21
21
 
22
- def self.list params = {}
22
+ def list params = {}
23
23
  validate Docker::API::InvalidParameter, [:all, :filters, :digests], params
24
- connection.get(build_path(["json"], params))
24
+ @connection.get(build_path(["json"], params))
25
25
  end
26
26
 
27
- def self.search params = {}
27
+ def search params = {}
28
28
  validate Docker::API::InvalidParameter, [:term, :limit, :filters], params
29
- connection.get(build_path(["search"], params))
29
+ @connection.get(build_path(["search"], params))
30
30
  end
31
31
 
32
- def self.tag name, params = {}
32
+ def tag name, params = {}
33
33
  validate Docker::API::InvalidParameter, [:repo, :tag], params
34
- connection.post(build_path([name, "tag"], params))
34
+ @connection.post(build_path([name, "tag"], params))
35
35
  end
36
36
 
37
- def self.prune params = {}
37
+ def prune params = {}
38
38
  validate Docker::API::InvalidParameter, [:filters], params
39
- connection.post(build_path(["prune"], params))
39
+ @connection.post(build_path(["prune"], params))
40
40
  end
41
41
 
42
- def self.remove name, params = {}
42
+ def remove name, params = {}
43
43
  validate Docker::API::InvalidParameter, [:force, :noprune], params
44
- connection.delete(build_path([name], params))
44
+ @connection.delete(build_path([name], params))
45
45
  end
46
46
 
47
- def self.export name, path = "exported_image"
47
+ def export name, path = "exported_image"
48
48
  file = File.open("/tmp/exported-image", "wb")
49
49
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
50
50
  file.write(chunk)
51
51
  end
52
- response = connection.request(method: :get, path: build_path([name, "get"]) , response_block: streamer)
52
+ response = @connection.request(method: :get, path: build_path([name, "get"]) , response_block: streamer)
53
53
  file.close
54
54
  response.status == 200 ? FileUtils.mv("/tmp/exported-image", File.expand_path(path)) : FileUtils.rm("/tmp/exported-image")
55
55
  response
56
56
  end
57
57
 
58
- def self.import path, params = {}
58
+ def import path, params = {}
59
59
  validate Docker::API::InvalidParameter, [:quiet], params
60
60
  file = File.open(File.expand_path(path), "r")
61
- response = connection.request(method: :post, path: build_path(["load"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
61
+ response = @connection.request(method: :post, path: build_path(["load"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
62
62
  file.close
63
63
  response
64
64
  end
65
65
 
66
- def self.push name, params = {}, authentication = {}
66
+ def push name, params = {}, authentication = {}
67
67
  validate Docker::API::InvalidParameter, [:tag], params
68
68
 
69
69
  if authentication.keys.size > 0
70
- auth = Docker::API::System.auth(authentication)
71
- return auth unless [200, 204].include? auth.status
72
- connection.request(method: :post, path: build_path([name, "push"], params), headers: { "X-Registry-Auth" => Base64.encode64(authentication.to_json.to_s).chomp } )
70
+ @connection.request(method: :post, path: build_path([name, "push"], params), headers: { "X-Registry-Auth" => Base64.urlsafe_encode64(authentication.to_json.to_s).chomp } )
73
71
  else
74
- connection.post(build_path([name, "push"], params))
72
+ raise Docker::API::Error.new("Provide authentication parameters to push an image")
75
73
  end
76
74
  end
77
75
 
78
- def self.commit params = {}, body = {}
76
+ def commit params = {}, body = {}
79
77
  validate Docker::API::InvalidParameter, [:container, :repo, :tag, :comment, :author, :pause, :changes], params
80
78
  validate Docker::API::InvalidRequestBody, Docker::API::CommitBody, body
81
- container = Docker::API::Container.inspect(params[:container])
79
+ container = Docker::API::Container.new.inspect(params[:container])
82
80
  return container if [404, 301].include? container.status
83
81
  body = JSON.parse(container.body)["Config"].merge(body)
84
- connection.request(method: :post, path: build_path("/commit", params), headers: {"Content-Type": "application/json"}, body: body.to_json)
82
+ @connection.request(method: :post, path: build_path("/commit", params), headers: {"Content-Type": "application/json"}, body: body.to_json)
85
83
  end
86
84
 
87
- def self.create params = {}, authentication = {}
85
+ def create params = {}, authentication = {}
88
86
  validate Docker::API::InvalidParameter, [:fromImage, :fromSrc, :repo, :tag, :message, :platform], params
89
87
 
90
88
  if authentication.keys.size > 0
91
- auth = Docker::API::System.auth(authentication)
89
+ auth = Docker::API::System.new.auth(authentication)
92
90
  return auth unless [200, 204].include? auth.status
93
- connection.request(method: :post, path: build_path(["create"], params), headers: { "X-Registry-Auth" => Base64.encode64(authentication.to_json.to_s).chomp } )
91
+ @connection.request(method: :post, path: build_path(["create"], params), headers: { "X-Registry-Auth" => Base64.encode64(authentication.to_json.to_s).chomp } )
94
92
  elsif params.has_key? :fromSrc
95
93
  if params[:fromSrc].match(/^(http|https)/)
96
- connection.request(method: :post, path: build_path(["create"], params))
94
+ @connection.request(method: :post, path: build_path(["create"], params))
97
95
  else
98
96
  file = File.open(File.expand_path(params[:fromSrc]), "r")
99
97
  params[:fromSrc] = "-"
100
- response = connection.request(method: :post, path: build_path(["create"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
98
+ response = @connection.request(method: :post, path: build_path(["create"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
101
99
  file.close
102
100
  response
103
101
  end
104
102
  else
105
- connection.post(build_path(["create"], params))
103
+ @connection.post(build_path(["create"], params))
106
104
  end
107
105
  end
108
106
 
109
- def self.build path, params = {}, authentication = {}
110
- raise Docker::API::InvalidRequestBody unless path || params[:remote]
107
+ def build path, params = {}, authentication = {}
108
+ raise Docker::API::Error.new("Expected path or params[:remote]") unless path || params[:remote]
111
109
  validate Docker::API::InvalidParameter, Docker::API::BuildParams, params
112
110
 
113
111
  header = {"Content-type": "application/x-tar"}
114
112
  if authentication.keys.size > 0
115
113
  authentication.each_key do |server|
116
- auth = Docker::API::System.auth({username: authentication[server][:username] ,password:authentication[server][:password], serveraddress: server})
114
+ auth = Docker::API::System.new.auth({username: authentication[server][:username] ,password:authentication[server][:password], serveraddress: server})
117
115
  return auth unless [200, 204].include? auth.status
118
116
  end
119
117
  header.merge!({"X-Registry-Config": Base64.urlsafe_encode64(authentication.to_json.to_s).chomp})
@@ -121,17 +119,17 @@ module Docker
121
119
 
122
120
  begin
123
121
  file = File.open( File.expand_path( path ) , "r")
124
- response = connection.request(method: :post, path: build_path("/build", params), headers: header, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s})
122
+ response = @connection.request(method: :post, path: build_path("/build", params), headers: header, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s})
125
123
  file.close
126
124
  rescue
127
- response = connection.request(method: :post, path: build_path("/build", params), headers: header)
125
+ response = @connection.request(method: :post, path: build_path("/build", params), headers: header)
128
126
  end
129
127
  response
130
128
  end
131
129
 
132
- def self.delete_cache params = {}
130
+ def delete_cache params = {}
133
131
  validate Docker::API::InvalidParameter, [:all, "keep-storage", :filters], params
134
- connection.post(build_path("/build/prune", params))
132
+ @connection.post(build_path("/build/prune", params))
135
133
  end
136
134
 
137
135
  end