ncc-api 1.0.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: 21ef6133b98ada6201561ea8663c154440c67466
4
+ data.tar.gz: 471fa9289f03247944859d225e3e4c2c0b10f1f5
5
+ SHA512:
6
+ metadata.gz: eb895ca7b2f897899ef0082a5ef3d7f705fdb42524ef66a66bdfc0da1004b9d3ffd1125a92e59f3abce60c63f0310d9325528f0005cf3fc53d0a4cf8a10272d5
7
+ data.tar.gz: aafb44f8fa87becc08dd41dc1975598e55ef49f96c6fe34acef56d19d560b2d335515b9f7deb5fc8309b872405b01a23f1f5adb3185dd657057ee7148180fb96
data/bin/ncc-api ADDED
@@ -0,0 +1,62 @@
1
+ #!/bin/bash
2
+ # /* Copyright 2013 Proofpoint, Inc. All rights reserved.
3
+ # Copyright 2014 Evernote Corporation. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ # */
17
+
18
+
19
+ app_path="${1:-$(dirname $0)/..}"
20
+ launchwith="${2:-rackup}"
21
+ rack_environment="${3:-development}"
22
+
23
+ for f in etc/ncc-api.ru lib/ncc.rb
24
+ do
25
+ if [ ! -f "$app_path/$f" ]
26
+ then
27
+ echo "Required file $app_path/$f not found" >&2
28
+ exit 1
29
+ fi
30
+ done
31
+
32
+ export NCCAPI_HOME="$app_path"
33
+
34
+ if [ "$launchwith" = rackup ]
35
+ then
36
+ # Rackup-based launch (default)
37
+ rackup="${RACKUP_PATH:-rackup}"
38
+
39
+ if [ ! -x "$rackup" ]
40
+ then
41
+ echo "Can't find rackup (no $rackup)" >&2
42
+ exit 2
43
+ fi
44
+
45
+ echo "$rackup" -p 9292 -I"$app_path/lib" -E "$rack_environment" "$app_path/etc/ncc-api.ru"
46
+ "$rackup" -p 9292 -I"$app_path/lib" -E "$rack_environment" "$app_path/etc/ncc-api.ru"
47
+ elif [ "$launchwith" = unicorn ]
48
+ then
49
+ unicorn="${UNICORN_PATH:-unicorn}"
50
+
51
+ if [ ! -x "$unicorn" ]
52
+ then
53
+ echo "Can't find unicorn (no $unicorn)" >&2
54
+ exit 2
55
+ fi
56
+ echo "$unicorn" -I"$app_path/lib" -D -E "$rack_environment" -c "$app_path/etc/unicorn.cfg" "$app_path/etc/ncc-api.ru"
57
+ "$unicorn" -I"$app_path/lib" -D -E "$rack_environment" -c "$app_path/etc/unicorn.cfg" "$app_path/etc/ncc-api.ru"
58
+ else
59
+ # TODO: Need to specify environment here
60
+ echo "Can't launch with $launchwith" >&2
61
+ exit 3
62
+ fi
data/lib/ncc/config.rb ADDED
@@ -0,0 +1,207 @@
1
+ #!ruby
2
+ # /* Copyright 2013 Proofpoint, Inc. All rights reserved.
3
+ # Copyright 2014 Evernote Corporation. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ # */
17
+
18
+
19
+ require 'rubygems'
20
+ require 'json'
21
+
22
+ class NCC
23
+
24
+ end
25
+
26
+ class NCC::Config
27
+ include Enumerable
28
+
29
+ Infinite = +1.0/0.0 # Not really *math*, per se, but Lewis
30
+ # Carroll would have liked it
31
+ # -jbrinkley/20130403
32
+
33
+ attr_reader :mtime, :file
34
+
35
+ def initialize(source = ["/etc/ncc-api",
36
+ "#{ENV['NCCAPI_HOME']}/etc"], opt={})
37
+ @opt = opt
38
+ @file = { }
39
+ @mtime = nil
40
+ source = [source] unless source.respond_to? :select
41
+ @file = source.select { |f| File.exist? f }.first
42
+ unless @file
43
+ raise ArgumentError.new("Can't locate configuration in " +
44
+ "#{source.inspect}")
45
+ end
46
+ debug "Creating configuration from #{@file}"
47
+ update_config
48
+ end
49
+
50
+ def opt(optname)
51
+ if @opt.has_key? optname
52
+ @opt[optname]
53
+ else
54
+ nil
55
+ end
56
+ end
57
+
58
+ def update_config(tolerant=false)
59
+ debug "updating config"
60
+ if File.directory? @file
61
+ debug "#{@file} is a directory"
62
+ @data ||= { }
63
+ data = { }
64
+ Dir.open(@file) do |dirh|
65
+ @mtime = File.stat(@file).mtime
66
+ debug "storing mtime: #{@mtime}"
67
+ dirh.each do |entry|
68
+ debug "considering #{entry}"
69
+ next if entry[0] == "."[0]
70
+ if File.directory? File.join(@file, entry) or
71
+ m = /(.*)\.conf$/.match(entry)
72
+ debug "#{entry} is further configuration"
73
+ key = m.nil? ? entry.intern : m[1]
74
+ if @data.has_key? key
75
+ data[key] = @data[key]
76
+ else
77
+ data[key] =
78
+ NCC::Config.new(File.join(@file, entry),
79
+ @opt)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ @data = data
85
+ else
86
+ debug "#{@file} is not a directory"
87
+ begin
88
+ File.open(@file, 'r') do |fh|
89
+ @mtime = fh.stat.mtime
90
+ @data = JSON.load(fh)
91
+ end
92
+ rescue Errno::ENOENT
93
+ @mtime = Time.now
94
+ @data = { }
95
+ rescue JSON::ParserError => err
96
+ do_warn "Error parsing JSON in #{@file}, not updating config"
97
+ end
98
+ end
99
+ end
100
+
101
+ def do_warn(msg)
102
+ if opt :logger
103
+ opt(:logger).warn msg
104
+ end
105
+ end
106
+
107
+ def debug(msg)
108
+ if opt(:logger) and opt(:logger).respond_to? :debug
109
+ opt(:logger).debug "#<#{me}>: #{msg}"
110
+ end
111
+ end
112
+
113
+ def me
114
+ "#{self.class}:#{@file}"
115
+ end
116
+
117
+ def to_s
118
+ "#<#{me} #{@data.inspect}>"
119
+ end
120
+
121
+ def to_hash(*keys)
122
+ update_config unless current?
123
+ if keys.length > 0
124
+ Hash[
125
+ @data.select do |k, v|
126
+ keys.include? k
127
+ end.map { |k, v| [k, value_of(v)] }
128
+ ]
129
+ else
130
+ Hash[
131
+ @data.map { |k, v| [k, value_of(v)] }
132
+ ]
133
+ end
134
+ end
135
+
136
+ def to_array(*keys)
137
+ update_config unless current?
138
+ if keys.length > 0
139
+ @data.select do |k, v|
140
+ keys.include? k
141
+ end.map { |k, v| value_of(v) }
142
+ else
143
+ @data.map { |k, v| value_of(v) }
144
+ end
145
+ end
146
+
147
+ def value_of(v)
148
+ if v.respond_to? :to_hash
149
+ v.to_hash
150
+ else
151
+ v
152
+ end
153
+ end
154
+
155
+ def to_json
156
+ self.to_hash.to_json
157
+ end
158
+
159
+ def current?
160
+ debug "checking currency (mtime=#{@mtime} " +
161
+ "file=#{File.exist?(@file) ? File.stat(@file).mtime : nil})"
162
+ case opt :staleness_threshold
163
+ when 0, nil
164
+ debug "staleness_threshold is 0 or nil, always checking"
165
+ File.exist? @file and @mtime >= File.stat(@file).mtime
166
+ when Infinite
167
+ true
168
+ else
169
+ Time.now <= (@mtime + opt(:staleness_threshold)) or
170
+ (File.exist? @file and @mtime >= File.stat(@file).mtime)
171
+ end
172
+ end
173
+
174
+ def [](key)
175
+ update_config unless current?
176
+ @data[key]
177
+ end
178
+
179
+ def []=(key, value)
180
+ update_config unless current?
181
+ @mtime = Time.now
182
+ @data[key] = value
183
+ end
184
+
185
+ def has_key?(key)
186
+ update_config unless current?
187
+ @data.has_key? key
188
+ end
189
+
190
+ def keys
191
+ update_config unless current?
192
+ @data.keys
193
+ end
194
+
195
+ def each
196
+ update_config unless current?
197
+ @data.each do |k, v|
198
+ yield k, v
199
+ end
200
+ end
201
+
202
+ def delete(k)
203
+ update_config unless current?
204
+ @data.delete(k)
205
+ end
206
+
207
+ end
@@ -0,0 +1,138 @@
1
+ #!ruby
2
+ # /* Copyright 2013 Proofpoint, Inc. All rights reserved.
3
+ # Copyright 2014 Evernote Corporation. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ # */
17
+
18
+ class NCC::Connection::AWS < NCC::Connection
19
+
20
+ def translate_size(flavor)
21
+ generic_translate(:size, flavor).
22
+ merge({
23
+ 'ram' => flavor.ram,
24
+ 'disk' => flavor.disk,
25
+ 'cores' => flavor.cores,
26
+ 'description' => flavor.name
27
+ })
28
+ end
29
+
30
+ def translate_image(provider_image)
31
+ generic_translate(:image, provider_image).
32
+ merge({
33
+ 'description' => provider_image.description,
34
+ 'ramdisk_id' => provider_image.ramdisk_id,
35
+ 'kernel_id' => provider_image.kernel_id
36
+ })
37
+ end
38
+
39
+ def provider
40
+ 'aws'
41
+ end
42
+
43
+ def use_only_mapped(type)
44
+ case type
45
+ when :image
46
+ true
47
+ when :size
48
+ false
49
+ end
50
+ end
51
+
52
+ def connection_params
53
+ ['aws_access_key_id', 'aws_secret_access_key']
54
+ end
55
+
56
+ def instance_name(server)
57
+ if ! server.tags.nil? and server.tags.has_key? 'Name'
58
+ server.tags['Name']
59
+ else
60
+ nil
61
+ end
62
+ end
63
+
64
+ def instance_size(server)
65
+ map_from_raw_provider_id(:size, server.flavor_id)
66
+ end
67
+
68
+ def instance_image(server)
69
+ map_from_raw_provider_id(:image, server.image_id)
70
+ end
71
+
72
+ def instance_status(server)
73
+ map_to_status(server.state)
74
+ end
75
+
76
+ def instance_ip_address(server)
77
+ server.private_ip_address
78
+ end
79
+
80
+ def console_log(instance_id)
81
+ begin
82
+ @fog.get_console_output(instance_id).body
83
+ rescue Fog::Compute::AWS::NotFound
84
+ instance_not_found instance_id
85
+ rescue Exception => e
86
+ communication_error e.message
87
+ end
88
+ end
89
+
90
+ def provider_request_of(instance)
91
+ {
92
+ :name => instance.name,
93
+ :flavor_id => sizes(instance.size)['provider_id'],
94
+ :image_id => images(instance.image)['provider_id'],
95
+ :tags => { 'Name' => instance.name }
96
+ }.merge(instance.extra provider)
97
+ end
98
+
99
+ def map_to_status(aws_status)
100
+ case aws_status
101
+ when 'running'
102
+ 'active'
103
+ when 'pending'
104
+ 'build'
105
+ when 'terminated'
106
+ 'terminated'
107
+ when 'shutting-down'
108
+ 'shutting-down'
109
+ when 'stopping'
110
+ 'suspending'
111
+ when 'stopped'
112
+ 'suspend'
113
+ else
114
+ 'unknown'
115
+ end
116
+ end
117
+
118
+ def map_to_provider_status(abstract_status)
119
+ case abstract_status
120
+ when 'active', 'error', 'hard-reboot', 'reboot', 'provider-operation',
121
+ 'unknown', 'needs-verify'
122
+ 'running'
123
+ when 'build'
124
+ 'pending'
125
+ when 'terminated'
126
+ 'terminated'
127
+ when 'shutting-down'
128
+ 'shutting-down'
129
+ when 'suspending'
130
+ 'stopping'
131
+ when 'suspend'
132
+ 'stopped'
133
+ else
134
+ 'running'
135
+ end
136
+ end
137
+
138
+ end
@@ -0,0 +1,171 @@
1
+ #!ruby
2
+ # /* Copyright 2013 Proofpoint, Inc. All rights reserved.
3
+ # Copyright 2014 Evernote Corporation. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ # */
17
+
18
+ class NCC::Connection::OpenStack < NCC::Connection
19
+
20
+ def size_id_field
21
+ :name
22
+ end
23
+
24
+ def image_id_field
25
+ :name
26
+ end
27
+
28
+ def translate_size(flavor)
29
+ generic_translate(:size, flavor).
30
+ merge({
31
+ 'ram' => flavor.ram,
32
+ 'cores' => flavor.vcpus,
33
+ 'description' => size_desc(flavor),
34
+ 'disk' => flavor.disk + flavor.ephemeral
35
+ })
36
+ end
37
+
38
+ def translate_image(pimage)
39
+ image = generic_translate(:image, pimage)
40
+ pimage.metadata.each do |metadatum|
41
+ if %(ramdisk_id kernel_id).include? metadatum.key
42
+ image[metadatum.key] = metadatum.value
43
+ end
44
+ end
45
+ image
46
+ end
47
+
48
+ def provider
49
+ 'openstack'
50
+ end
51
+
52
+ def size_desc(f)
53
+ (f.vcpus > 1 ? "#{f.vcpus}CPU " : "") +
54
+ "#{(f.ram / 1024).round}GB RAM #{f.disk + f.ephemeral}GB disk"
55
+ end
56
+
57
+ def connection_params
58
+ ['openstack_auth_url', 'openstack_username', 'openstack_api_key']
59
+ end
60
+
61
+ def map_to_status(provider_status)
62
+ case provider_status
63
+ when 'ACTIVE', 'PASSWORD', 'SHUTOFF'
64
+ 'active'
65
+ when 'BUILD'
66
+ 'build'
67
+ when 'DELETED'
68
+ 'terminated'
69
+ when 'ERROR'
70
+ 'error'
71
+ when 'HARD_REBOOT'
72
+ 'hard-reboot'
73
+ when 'REBOOT'
74
+ 'reboot'
75
+ when 'REBUILD', 'RESCUE', 'RESIZE', 'REVERT_RESIZE'
76
+ 'provider-operation'
77
+ when 'SUSPENDED'
78
+ 'suspend'
79
+ when 'UNKNOWN'
80
+ 'unknown'
81
+ when 'VERIFY_RESIZE'
82
+ 'needs-verify'
83
+ else
84
+ 'unknown'
85
+ end
86
+ end
87
+
88
+ def instance_image(server)
89
+ map_from_raw_provider_id(:image, server.image['id'])
90
+ end
91
+
92
+ def instance_size(server)
93
+ map_from_raw_provider_id(:size, server.flavor['id'])
94
+ end
95
+
96
+ def instance_status(server)
97
+ map_to_status(server.state)
98
+ end
99
+
100
+ def instance_ip_address(server)
101
+ instance_network_names = ['instance_net', 'instnace_net']
102
+ if !server.addresses.nil?
103
+ instance_network_names.each do |network_name|
104
+ if server.addresses.has_key? network_name
105
+ return server.addresses[network_name].first['addr']
106
+ end
107
+ end
108
+ end
109
+ return nil
110
+ end
111
+
112
+ def instance_host(server)
113
+ server.os_ext_srv_attr_host
114
+ end
115
+
116
+ def console_log(instance_id)
117
+ server = @fog.servers.get(instance_id)
118
+ if server.nil?
119
+ instance_not_found instance_id
120
+ else
121
+ begin
122
+ server.console.body
123
+ rescue Exception => e
124
+ communication_error e.message
125
+ end
126
+ end
127
+ end
128
+
129
+ def provider_request_of(instance)
130
+ {
131
+ :name => instance.name,
132
+ :flavor_ref => sizes(instance.size)['provider_id'],
133
+ :image_ref => images(instance.image)['provider_id'],
134
+ }.merge(instance.extra provider)
135
+ end
136
+
137
+ def reboot(instance_id)
138
+ server = @fog.servers.get(instance_id)
139
+ if server.nil?
140
+ instance_not_found instance_id
141
+ else
142
+ server.reboot 'HARD'
143
+ end
144
+ end
145
+
146
+ def map_to_provider_status(abstract_status)
147
+ case abstract_status
148
+ when 'active', 'provider-operation', 'shutting-down', 'suspending'
149
+ 'ACTIVE'
150
+ when 'build'
151
+ 'BUILD'
152
+ when 'terminated'
153
+ 'DELETED'
154
+ when 'error'
155
+ 'ERROR'
156
+ when 'hard-reboot'
157
+ 'HARD_REBOOT'
158
+ when 'reboot'
159
+ 'REBOOT'
160
+ when 'suspend'
161
+ 'SUSPENDED'
162
+ when 'unknown'
163
+ 'UNKNOWN'
164
+ when 'needs-verify'
165
+ 'VERIFY_RESIZE'
166
+ else
167
+ 'UNKNOWN'
168
+ end
169
+ end
170
+
171
+ end