kldockeragent 0.5.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.
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: []