kldockeragent 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c505ae9e35342dd34f178dec5018675e204693b
4
+ data.tar.gz: 3853d4786fe5aadf6f631cd5cc461b7c8c5a27ee
5
+ SHA512:
6
+ metadata.gz: 2854427e5768d19dfc27896c8dea49745f81420479cc0ac7cb18003f1a831b2a63d9cb6fd2180df41fc0450274aeb6d441db43503512bb92b469af519f83e411
7
+ data.tar.gz: 652d7b08dbe57af4e2f0e272937eed0b29a9c6a80eb07669359a6082ed8b844fbaf70c1f481ce2f5cd45d73290cf9f77bf6fb137dcc82201113fad79bcf5421e
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ config/server-registry.json
2
+ *.gem
data/Rakefile ADDED
File without changes
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
data/bin/kldockeragent ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/kldockeragent'
4
+
5
+ def version?
6
+ File.read(File.dirname(__FILE__) + '/../VERSION').strip
7
+ end
8
+
9
+ opts = Trollop::options do
10
+ version "kldockeragent #{version?} (c) 2016 Kytoonlabs"
11
+ banner <<-EOS
12
+ Agent for KL Docker Agent configuration management tool.
13
+ Usage:
14
+ kldockeragent [options]
15
+
16
+ where [options] are:
17
+ EOS
18
+ opt :start, "Start the agent. If --daemon option is set true, then the agent will start as a daemon.", :short => '-s'
19
+ opt :stop, "Stop the daemon agent.", :short => '-t'
20
+ opt :restart, "Restart the daemon agent.", :short => '-r'
21
+ opt :status, "Print the status of the daemon agent.", :short => '-a'
22
+ end
23
+
24
+ if opts[:start]
25
+ if not KL::Agent.pid.nil?
26
+ puts "Agent is already running with PID #{KL::Agent.pid}"
27
+ else
28
+ opts[:daemon] = true
29
+ KL::Agent.start(opts)
30
+ end
31
+ elsif opts[:stop]
32
+ KL::Agent.stop(opts)
33
+ elsif opts[:restart]
34
+ KL.logger.info '[main] Restart Agent command detected.'
35
+ opts[:daemon] = true
36
+ KL::Agent.stop if KL::Agent.pid.to_i > 0
37
+ KL::Agent.start(opts)
38
+ elsif opts[:status]
39
+ KL::Agent.status
40
+ end
41
+
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'kldockeragent'
3
+ s.version = File.read(File.dirname(__FILE__) + '/VERSION').strip
4
+ s.date = File.mtime(File.dirname(__FILE__) + '/VERSION').strftime("%Y-%m-%d")
5
+ s.summary = "KL DockerAgent"
6
+ s.description = "Agent with REST Api to send information about docker containers"
7
+ s.authors = ["Kytoonlabs"]
8
+ s.email = 'admin@kytoonlabs.com'
9
+ s.homepage = 'https://kytoonlabs.com/tools/docker-agent'
10
+ s.license = 'MIT'
11
+ s.require_paths = ['lib']
12
+ s.executables << 'kldockeragent'
13
+ s.files = `git ls-files`.split("\n").select { |n| !(n =~ /^(modules|test)\/.*/) }
14
+ end
@@ -0,0 +1,151 @@
1
+ require 'rubygems'
2
+ require 'thread'
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'logger'
6
+ require 'json'
7
+ require 'digest/md5'
8
+ require 'shellwords'
9
+ require 'trollop'
10
+ require 'socket'
11
+
12
+ module KL
13
+ module Agent
14
+
15
+ PIDFile = "#{KL.home}/kldockeragent.pid"
16
+
17
+ @@notifier_engine = KL::Notifier.new
18
+ @@web_server = KL::Server.new
19
+
20
+ @@global_app_uuid = KL.getApplicationId
21
+
22
+ # Start the agent.
23
+ #
24
+ # options:
25
+ # :daemon => true if running as a daemon, false if as a console application
26
+ # :port => port of web server will listen to
27
+ # :ssl => set true to enable HTTPS
28
+ # :certfile => certificate file path for HTTPS
29
+ # :keyfile => key file path for HTTPS
30
+ #
31
+ def self.start(opts={})
32
+ KL.logger.info "[main] Starting agent: #{KL::Agent.getUUID}"
33
+ puts "Starting agent..."
34
+
35
+ @@config = opts
36
+
37
+ Process.daemon if opts[:daemon] and not opts[:mock]
38
+ begin
39
+ if not is_windows
40
+ # trap signal
41
+ ['INT', 'KILL', 'HUP'].each do |signal|
42
+ trap(signal) {
43
+ KL.logger.info "[main] Shutting down services..."
44
+ web_server.stop
45
+ notifier_engine.stop
46
+ loop do
47
+ break if notifier_engine.status == :stopped
48
+ sleep 1
49
+ end
50
+
51
+ self.close
52
+ }
53
+ end
54
+ end
55
+
56
+ File.open(PIDFile, 'w', 0644) { |f| f.write($$.to_s) }
57
+
58
+ notifier_engine.start
59
+ web_server.start
60
+
61
+ @@main_enabled = true
62
+ while @@main_enabled; end
63
+
64
+ rescue Exception => e
65
+ KL.logger.error "Starting the agent [Failed] #{e}\n#{e.backtrace.join("\n")}"
66
+ raise e
67
+ end
68
+ end
69
+
70
+ # Stop the agent's daemon.
71
+ #
72
+ def self.stop(opts={})
73
+ begin
74
+ pid = File.read(PIDFile).to_i
75
+ puts "Stopping agent with PID #{pid}..."
76
+ Process.kill 'HUP', pid
77
+
78
+ if not opts[:mock]
79
+ begin
80
+ sleep (KL::Notifier::SleepTime + 0.5)
81
+
82
+ Process.kill 0, pid
83
+ KL.logger.info "[main] Agent is still running."
84
+ puts "Agent is still running."
85
+
86
+ KL.logger.info "[main] Killing agent."
87
+ puts "Killing agent."
88
+ Process.kill 9, pid
89
+ rescue
90
+ KL.logger.info "[main] Agent has stopped."
91
+ puts "Agent has stopped."
92
+ File.delete(PIDFile) if File.exist?(PIDFile)
93
+ end
94
+ end
95
+
96
+ rescue
97
+ puts "Agent is not running."
98
+ File.delete(PIDFile) if File.exist?(PIDFile)
99
+ end
100
+ end
101
+
102
+ def self.pid
103
+ begin
104
+ pid = File.read(PIDFile).to_i
105
+ return pid if Process.kill 0, pid
106
+ rescue
107
+ end
108
+ nil
109
+ end
110
+
111
+ # Return agent's PID if it is running, otherwise nil.
112
+ #
113
+ def self.status
114
+ if pid.nil?
115
+ puts "Agent is not running."
116
+ else
117
+ puts "Agent is running with PID #{pid}"
118
+ end
119
+ end
120
+
121
+ def self.is_windows
122
+ (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
123
+ end
124
+
125
+ def self.is_linux
126
+ (RbConfig::CONFIG['host_os'] =~ /linux/)
127
+ end
128
+
129
+ def self.notifier_engine
130
+ @@notifier_engine
131
+ end
132
+
133
+ def self.web_server
134
+ @@web_server
135
+ end
136
+
137
+ def self.close
138
+ KL.logger.info "[main] Stopping agent..."
139
+ @@main_enabled = false
140
+ end
141
+
142
+ def self.getUUID
143
+ @@global_app_uuid
144
+ end
145
+
146
+ def self.getVersion
147
+ File.read(File.dirname(__FILE__) + '/../../VERSION').strip
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,51 @@
1
+ require 'sinatra'
2
+ require 'webrick'
3
+ require 'webrick/https'
4
+ require 'webrick/ssl'
5
+ require 'openssl'
6
+ require 'json'
7
+
8
+ class KL::Api < Sinatra::Base
9
+
10
+ before do
11
+ halt 503 if !KL.server_registered
12
+ pass if ['ping'].include? request.path_info.split('/')[1]
13
+ if env['HTTP_AUTHORIZATION'] && env['HTTP_AUTHORIZATION'].split(':').length == 2
14
+ auth_header = env['HTTP_AUTHORIZATION'].split(':')
15
+ else
16
+ halt 401
17
+ end
18
+ public_key = auth_header[0]
19
+ signature = auth_header[1]
20
+
21
+ registry = KL.getRegistryData
22
+
23
+ halt 403 if public_key != registry['server']
24
+
25
+ data = request.path
26
+ data = "#{data}?#{request.query_string}" if request.query_string != ""
27
+
28
+ if ['POST', 'PUT', 'PATCH'].include? request.request_method
29
+ request.body.rewind
30
+ data += request.body.read
31
+ end
32
+
33
+ computed_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), registry['agent'], data)
34
+ if computed_signature == signature
35
+ KL.logger.info "[api] #{request.request_method} #{request.path}"
36
+ pass
37
+ else
38
+ halt 403
39
+ end
40
+ end
41
+
42
+ get '/ping' do
43
+ KL.logger.info "[api] Pong! to #{request.ip}"
44
+ 'Pong: ' + `date`
45
+ end
46
+
47
+ get '/api/v1/applications' do
48
+ KL.publish_json(KL.Request_getDockerListAllApplications)
49
+ end
50
+ end
51
+
@@ -0,0 +1,34 @@
1
+ module KL
2
+ def KL.Command_registerNewAgent
3
+ KL.logger.info '[command] Registering new agent...'
4
+ agent = KL::Agent.getUUID
5
+ data = KL.prepare_agent_register_data(agent)
6
+ uri = "#{KL.config['notifier']['server']}/api/1.0/nodes/register"
7
+ res = KL.net_post_json(uri, data.to_json)
8
+
9
+ if res.is_a?(Net::HTTPSuccess)
10
+ response = JSON.parse(res.body)
11
+ registry = {
12
+ agent: agent,
13
+ server: response['code']
14
+ }
15
+ KL.create_registry_file(registry)
16
+ KL.logger.info '[command] Agent registration has been completed correctly'
17
+ else
18
+ KL.logger.info '[command] An error has occurred trying to register agent.'
19
+ end
20
+ end
21
+
22
+ def KL.Command_notifyStatus
23
+ KL.logger.info '[command] Sending instance update...'
24
+ data = KL.prepare_agent_data
25
+ uri = "#{KL.config['notifier']['server']}/api/1.0/nodes/data"
26
+ headers_auth = KL.getAuthorizationHeaders('/api/1.0/nodes/data', data.to_json)
27
+ res = KL.net_post_json(uri, data.to_json, headers_auth)
28
+ if res.is_a?(Net::HTTPSuccess)
29
+ KL.logger.info '[command] Agent notification successfully executed.'
30
+ else
31
+ KL.logger.info '[command] An error has occurred trying to notify update.'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,154 @@
1
+ require 'webrick'
2
+ require 'webrick/https'
3
+ require 'usagewatch'
4
+
5
+ module KL
6
+ def KL.create_default_config(file, logger)
7
+ File.open(file, File::RDWR|File::CREAT, 0644) { |f|
8
+ f.flock(File::LOCK_EX)
9
+ value = '{"api":{"port":3344,"ssl":false},"notifier":{"server":"https://hub.kytoonlabs.com:4444","rate":120}}'
10
+ f.rewind
11
+ f.write(value.to_s)
12
+ f.flush
13
+ f.truncate(f.pos)
14
+ }
15
+ logger.info "[config] Default config file has been created at {#{file}}" if logger
16
+ end
17
+
18
+ def KL.logger
19
+ home = KL.home
20
+ Dir.mkdir(home, 0700) if not File.exist?(home)
21
+ logFile = "#{home}/kldockeragent.log"
22
+ log = WEBrick::Log.new(logFile, WEBrick::BasicLog::INFO ||
23
+ WEBrick::BasicLog::ERROR ||
24
+ WEBrick::BasicLog::FATAL ||
25
+ WEBrick::BasicLog::WARN)
26
+ log
27
+ end
28
+
29
+ def KL.home
30
+ home = ((Process.euid == 0 and File.directory?('/var')) ? '/var/kldockerpagent' : File.expand_path(Dir.home + '/.kldockeragent'))
31
+ home
32
+ end
33
+
34
+ def KL.config
35
+ configFile = "#{KL.home}/kldockeragent.json"
36
+ KL.create_default_config(configFile, KL.logger) if !File.exist?(configFile)
37
+ config_json = File.read(configFile)
38
+ config_data = JSON.parse(config_json)
39
+ config_data
40
+ end
41
+
42
+ def KL.server_registered
43
+ registryFile = "#{KL.home}/kldockeragent.registry"
44
+ reg = File.exist?(registryFile)
45
+ reg
46
+ end
47
+
48
+ def KL.publish_json(json_object, st = 200)
49
+ status st
50
+ content_type :json
51
+ json_object.to_json
52
+ end
53
+ def KL.prepare_agent_register_data(hash)
54
+ data = {
55
+ private_hash: hash,
56
+ hostname: `hostname`.gsub("\n",""),
57
+ system_info: `uname -a`.gsub("\n",""),
58
+ docker_version: `docker --version`.gsub("\n","").gsub("Docker version ",""),
59
+ agent_version: KL::Agent.getVersion,
60
+ ip_address: IPSocket.getaddress(Socket.gethostname)
61
+ }
62
+ data
63
+ end
64
+
65
+ def KL.create_registry_file(data)
66
+ file = "#{KL.home}/kldockeragent.registry"
67
+ File.open(file, File::RDWR|File::CREAT, 0644) { |f|
68
+ f.flock(File::LOCK_EX)
69
+ f.rewind
70
+ f.write(data.to_json.to_s)
71
+ f.flush
72
+ f.truncate(f.pos)
73
+ }
74
+ logger.info "[config] Registry config file has been created at {#{file}}" if logger
75
+ end
76
+
77
+ def KL.getRegistryData
78
+ json_reg = JSON.parse(File.open("#{KL.home}/kldockeragent.registry").read)
79
+ json_reg
80
+ end
81
+
82
+ def KL.getApplicationId
83
+ if (KL.server_registered)
84
+ registry = KL.getRegistryData
85
+ id = registry['agent']
86
+ else
87
+ id = SecureRandom.uuid
88
+ end
89
+ id
90
+ end
91
+
92
+ def KL.getAuthorizationHeaders(url, json_data)
93
+ headers = {}
94
+ if (KL.server_registered)
95
+ registry = KL.getRegistryData
96
+ hash_data = KL.generateHash(url, json_data, registry['agent'])
97
+ auth_code = "#{registry['server']}:#{hash_data}"
98
+ headers = {}
99
+ headers['Authorization'] = auth_code
100
+ end
101
+ headers
102
+ end
103
+
104
+ def KL.generateHash(url, json_data, private_key)
105
+ data = "#{url}#{json_data}"
106
+ hash_data = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), private_key, data)
107
+ hash_data
108
+ end
109
+
110
+ def KL.prepare_agent_data
111
+ data = {
112
+ system_info: KL.Request_getSystemInfo,
113
+ docker_info: KL.Request_getDockerListAllApplications,
114
+ agent_version: KL::Agent.getVersion
115
+ }
116
+ data
117
+
118
+ end
119
+
120
+ def KL.Request_getDockerListAllApplications
121
+ filter = '_klac'
122
+ apps = Array.new
123
+ query = `docker ps --filter="name=#{filter}" --format="{{.ID}}::{{.Names}}::{{.Status}}::{{.Image}}"`
124
+ query.gsub!(/\r\n?/, "\n")
125
+ query.each_line do |line|
126
+ data = line.split('::')
127
+ apps.push({
128
+ 'id' => data[0],
129
+ 'name' => data[1].gsub(filter,""),
130
+ 'status'=>data[2],
131
+ 'image'=>data[3].gsub("\n","")
132
+ })
133
+ end
134
+ return apps
135
+ end
136
+
137
+ def KL.Request_getSystemInfo
138
+ usw = Usagewatch
139
+ info = {}
140
+ if KL::Agent.is_linux
141
+ info['os'] = 'linux'
142
+ info['diskused'] = usw.uw_diskused
143
+ info['diskused_perc'] = usw.uw_diskused_perc
144
+ info['cpuused'] = usw.uw_cpuused
145
+ info['memused'] = usw.uw_memused
146
+ info['cputop'] = usw.uw_cputop
147
+ info['memtop'] = usw.uw_memtop
148
+ else
149
+ info['os'] = 'not linux'
150
+ end
151
+ info
152
+ end
153
+
154
+ end
@@ -0,0 +1,23 @@
1
+ require 'net/http'
2
+
3
+ module KL
4
+ def KL.net_post_json(url, data, headers = [])
5
+ KL.logger.info "[net] Posting JSON to #{url} => #{data}"
6
+
7
+ uri = URI(url)
8
+
9
+ req = Net::HTTP::Post.new(uri)
10
+ req.content_type = 'application/json'
11
+ req.body = data
12
+ req['X-Kytoonlabs-Docker-Agent'] = KL::Agent.getUUID[0..13].gsub(/\s\w+\s*$/, '')
13
+ headers.each do |key, value|
14
+ req[key] = value
15
+ end
16
+
17
+ Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
18
+ @res = http.request req
19
+ end
20
+ KL.logger.info "[net] Response Code: #{@res.code}, Response Body: #{@res.body}"
21
+ @res
22
+ end
23
+ end
@@ -0,0 +1,106 @@
1
+ require 'thread'
2
+
3
+ class KL::Notifier
4
+
5
+ SleepTime = 10
6
+
7
+ NotifierLockFile = "#{KL.home}/notifier.lock.#{Time.now.to_i}"
8
+
9
+ attr_reader :enabled, :status, :mode
10
+
11
+ def initialize(p={})
12
+ @lock = Mutex.new
13
+ @enabled = false
14
+ @status = :stopped
15
+ @index = 0
16
+ config = KL.config
17
+ @rate = config['notifier']['rate']
18
+ end
19
+
20
+ def stop
21
+ @enabled = false
22
+ end
23
+
24
+ def start
25
+ @enabled = true
26
+ @lock.synchronize {
27
+ return if @status == :running
28
+ @status = :running
29
+ }
30
+ Thread.new {
31
+ begin
32
+ register_notifier_thread(:reset)
33
+
34
+ system("rm -f #{KL.home}/operator.*.lock")
35
+ system("rm -f #{KL.home}/notifier.lock.*")
36
+
37
+ KL.logger.info "[notifier] Notifier engine is running"
38
+
39
+ self.run_notifier
40
+
41
+ File.delete(NotifierLockFile) if File.exist?(NotifierLockFile)
42
+ KL.logger.info "[notifier] Notifier engine has stopped."
43
+
44
+ rescue Exception => exp
45
+ KL.logger.error "Cannot start notifier engine => #{exp}\n#{exp.backtrace.join("\n")}"
46
+ end
47
+ @status = :stopped
48
+ }
49
+ end
50
+
51
+ def run_notifier
52
+ KL.logger.info "[notifier] Executing notifier engine every #{@rate} seconds"
53
+ while @enabled
54
+ begin
55
+ wait_for_notifier?
56
+
57
+ notify_data
58
+
59
+ sleep SleepTime
60
+
61
+ rescue Exception => e
62
+ KL.logger.error "Error on executing notifier \n#{e}\n#{e.backtrace.join("\n")}"
63
+ sleep SleepTime
64
+ end
65
+ end
66
+ end
67
+
68
+ def register_notifier_thread(mode=nil)
69
+ File.open(NotifierLockFile, File::RDWR|File::CREAT, 0644) { |f|
70
+ f.flock(File::LOCK_EX)
71
+ value = (mode == :reset ? 0 : (f.read.to_i + 1))
72
+ f.rewind
73
+ f.write(value.to_s)
74
+ f.flush
75
+ f.truncate(f.pos)
76
+ }
77
+ end
78
+
79
+ def wait_for_notifier?
80
+ total_notifier = 1
81
+ loop do
82
+ total_notifier = (File.exist?(NotifierLockFile) ? File.read(NotifierLockFile).to_i : 0)
83
+ return if total_notifier <= 0 or not @enabled
84
+ sleep 1
85
+ end
86
+ end
87
+
88
+ def notify_data
89
+ KL.logger.info "[heartbeat]"
90
+ if KL.server_registered
91
+ if (@index <= (@rate/SleepTime))
92
+ @index = @index + 1
93
+ else
94
+ @index = 0
95
+ KL.logger.info "[notifier] Executing notifier..."
96
+ KL.Command_notifyStatus
97
+ KL.logger.info "[notifier] Notifier executed."
98
+ end
99
+ else
100
+ KL.logger.info "[notifier] Executing notifier..."
101
+ KL.Command_registerNewAgent
102
+ KL.logger.info "[notifier] Notifier executed."
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,43 @@
1
+ require 'webrick'
2
+ require 'webrick/https'
3
+ require 'openssl'
4
+ require 'rack/ssl'
5
+
6
+ class KL::Server
7
+ def initialize
8
+ @config = KL.config
9
+ if @config['api']['ssl']
10
+ options = {
11
+ :Port => @config['api']['port'],
12
+ :SSLEnable => true,
13
+ :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
14
+ :SSLCertificate => OpenSSL::X509::Certificate.new(File.open(@config['api']['cert']['crt']).read),
15
+ :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.open(@config['api']['cert']['key']).read),
16
+ :SSLCertName => [[ "CN", WEBrick::Utils::getservername ]]
17
+ }
18
+ else
19
+ options = {
20
+ :Port => @config['api']['port'],
21
+ :SSLEnable => false
22
+ }
23
+ end
24
+ @webrick_options = options
25
+ @server = ::Rack::Handler::WEBrick
26
+ end
27
+ def start
28
+ KL.logger.info '[server] Starting web server...'
29
+ Thread.new {
30
+ @server.run KL::Api, @webrick_options
31
+ }
32
+ if (@config['api']['ssl'])
33
+ msg = "[server] Web server listening on #{@config['api']['port']} with SSL"
34
+ else
35
+ msg = "[server] Web server listening on #{@config['api']['port']}"
36
+ end
37
+ KL.logger.info msg
38
+ end
39
+ def stop
40
+ @server.shutdown
41
+ KL.logger.info '[server] Web server has stopped.'
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ # external dependencies
2
+ require 'rubygems'
3
+ require 'json'
4
+
5
+ module KL
6
+ end
7
+
8
+ # internal dependencies
9
+ libdir = File.expand_path(File.dirname(__FILE__))
10
+
11
+ require libdir + '/kldockeragent/net.rb'
12
+ require libdir + '/kldockeragent/commands.rb'
13
+ require libdir + '/kldockeragent/helper.rb'
14
+ require libdir + '/kldockeragent/api.rb'
15
+ require libdir + '/kldockeragent/server.rb'
16
+ require libdir + '/kldockeragent/notifier.rb'
17
+ require libdir + '/kldockeragent/agent.rb'
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kldockeragent
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Kytoonlabs
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Agent with REST Api to send information about docker containers
14
+ email: admin@kytoonlabs.com
15
+ executables:
16
+ - kldockeragent
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - Rakefile
22
+ - VERSION
23
+ - bin/kldockeragent
24
+ - kldockeragent.gemspec
25
+ - lib/kldockeragent.rb
26
+ - lib/kldockeragent/agent.rb
27
+ - lib/kldockeragent/api.rb
28
+ - lib/kldockeragent/commands.rb
29
+ - lib/kldockeragent/helper.rb
30
+ - lib/kldockeragent/net.rb
31
+ - lib/kldockeragent/notifier.rb
32
+ - lib/kldockeragent/server.rb
33
+ homepage: https://kytoonlabs.com/tools/docker-agent
34
+ licenses:
35
+ - MIT
36
+ metadata: {}
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 2.0.14.1
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: KL DockerAgent
57
+ test_files: []