piculet 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ module Piculet
2
+ VERSION = "0.0.1"
3
+ end
4
+
5
+ Version = Piculet::VERSION
@@ -0,0 +1,18 @@
1
+ require 'piculet/wrapper/security-group-collection'
2
+
3
+ module Piculet
4
+ class EC2Wrapper
5
+ def initialize(ec2, options)
6
+ @ec2 = ec2
7
+ @options = options.dup
8
+ end
9
+
10
+ def security_groups
11
+ SecurityGroupCollection.new(@ec2.security_groups, @options)
12
+ end
13
+
14
+ def updated?
15
+ !!@options.updated
16
+ end
17
+ end # EC2Wrapper
18
+ end # Piculet
@@ -0,0 +1,134 @@
1
+ require 'piculet/logger'
2
+ require 'piculet/ext/ip-permission-collection-ext'
3
+ require 'piculet/wrapper/permission'
4
+
5
+ module Piculet
6
+ class EC2Wrapper
7
+ class SecurityGroupCollection
8
+ class SecurityGroup
9
+ class PermissionCollection
10
+ include Logger::ClientHelper
11
+
12
+ def initialize(security_group, direction, options)
13
+ @security_group = security_group
14
+ @permissions = security_group.send("#{direction}_ip_permissions")
15
+ @direction = direction
16
+ @options = options
17
+ end
18
+
19
+ def each
20
+ perm_list = @permissions ? @permissions.aggregate : []
21
+
22
+ perm_list.each do |perm|
23
+ yield(Permission.new(perm, self, @options))
24
+ end
25
+ end
26
+
27
+ def authorize(protocol, ports, *sources)
28
+ log(:info, " authorize #{format_sources(sources)}", :green)
29
+
30
+ unless @options.dry_run
31
+ sources = normalize_sources(sources)
32
+
33
+ case @direction
34
+ when :ingress
35
+ @security_group.authorize_ingress(protocol, ports, *sources)
36
+ @options.updated = true
37
+ when :egress
38
+ sources.push(:protocol => protocol, :ports => ports)
39
+ @security_group.authorize_egress(*sources)
40
+ @options.updated = true
41
+ end
42
+ end
43
+ end
44
+
45
+ def revoke(protocol, ports, *sources)
46
+ log(:info, " revoke #{format_sources(sources)}", :green)
47
+
48
+ unless @options.dry_run
49
+ sources = normalize_sources(sources)
50
+
51
+ case @direction
52
+ when :ingress
53
+ @security_group.revoke_ingress(protocol, ports, *sources)
54
+ @options.updated = true
55
+ when :egress
56
+ sources.push(:protocol => protocol, :ports => ports)
57
+ @security_group.revoke_egress(*sources)
58
+ @options.updated = true
59
+ end
60
+ end
61
+ end
62
+
63
+ def create(protocol, port_range, dsl)
64
+ dsl_ip_ranges = dsl.ip_ranges || []
65
+ dsl_groups = (dsl.groups || []).map do |i|
66
+ i.kind_of?(Array) ? i : [@options.ec2.owner_id, i]
67
+ end
68
+
69
+ sources = dsl_ip_ranges + dsl_groups
70
+
71
+ unless sources.empty?
72
+ log(:info, 'Create Permission', :cyan, "#{log_id} > #{protocol} #{port_range}")
73
+ authorize(protocol, port_range, *sources)
74
+ end
75
+ end
76
+
77
+ def log_id
78
+ vpc = @security_group.vpc_id || :classic
79
+ name = @security_group.name
80
+
81
+ unless @options.ec2.own?(@security_group.owner_id)
82
+ name = "#{@security_group.owner_id}/#{name}"
83
+ end
84
+
85
+ "#{vpc} > #{name}(#{@direction})"
86
+ end
87
+
88
+ private
89
+ def normalize_sources(sources)
90
+ normalized = []
91
+
92
+ sources.each do |src|
93
+ case src
94
+ when String
95
+ normalized << src
96
+ when Array
97
+ owner_id, group = src
98
+
99
+ unless group =~ /\Asg-[0-9a-f]\Z/
100
+ sg_coll = @options.ec2.security_groups.filter('group-name', group)
101
+ sg_coll = sg_coll.filter('vpc-id', @security_group.vpc_id) if @security_group.vpc?
102
+ sg_coll = sg_coll.filter('owner-id', owner_id) unless @options.ec2.own?(owner_id)
103
+
104
+ unless (sg = sg_coll.first)
105
+ raise "Can't find SecurityGroup: #{owner_id}/#{group} in #{@security_group.vpc_id || :classic}"
106
+ end
107
+
108
+ group = sg.id
109
+ end
110
+
111
+ normalized << {:user_id => owner_id, :group_id => group}
112
+ end
113
+ end
114
+
115
+ return normalized
116
+ end
117
+
118
+ def format_sources(sources)
119
+ sources.map {|src|
120
+ if src.kind_of?(Array)
121
+ owner_id, group = src
122
+ dst = [group]
123
+ dst.unshift(owner_id) unless @options.ec2.own?(owner_id)
124
+ dst.join('/')
125
+ else
126
+ src
127
+ end
128
+ }.join(', ')
129
+ end
130
+ end # PermissionCollection
131
+ end # SecurityGroup
132
+ end # SecurityGroupCollection
133
+ end # EC2Wrapper
134
+ end # Piculet
@@ -0,0 +1,92 @@
1
+ require 'forwardable'
2
+ require 'piculet/logger'
3
+
4
+ module Piculet
5
+ class EC2Wrapper
6
+ class SecurityGroupCollection
7
+ class SecurityGroup
8
+ class PermissionCollection
9
+ class Permission
10
+ extend Forwardable
11
+ include Logger::ClientHelper
12
+
13
+ def_delegators(
14
+ :@permission,
15
+ :protocol, :port_range, :ip_ranges, :groups)
16
+
17
+ def initialize(permission, collection, options)
18
+ @permission = permission
19
+ @collection = collection
20
+ @options = options
21
+ end
22
+
23
+ def eql?(dsl)
24
+ dsl_ip_ranges, dsl_groups, self_ip_ranges, self_groups = normalize_attrs(dsl)
25
+ (self_ip_ranges == dsl_ip_ranges) and (self_groups == dsl_groups)
26
+ end
27
+
28
+ def update(dsl)
29
+ log(:info, 'Update Permission', :green, log_id)
30
+
31
+ plus_ip_ranges, minus_ip_ranges, plus_groups, minus_groups = diff(dsl)
32
+
33
+ unless (plus_ip_ranges + plus_groups).empty?
34
+ @collection.authorize(protocol, port_range, *(plus_ip_ranges + plus_groups))
35
+ end
36
+
37
+ unless (minus_ip_ranges + minus_groups).empty?
38
+ @collection.revoke(protocol, port_range, *(minus_ip_ranges + minus_groups))
39
+ end
40
+ end
41
+
42
+ def delete
43
+ log(:info, 'Delete Permission', :red, log_id)
44
+
45
+ self_ip_ranges, self_groups = normalize_self_attrs
46
+
47
+ unless (self_ip_ranges + self_groups).empty?
48
+ @collection.revoke(protocol, port_range, *(self_ip_ranges + self_groups))
49
+ end
50
+ end
51
+
52
+ private
53
+ def log_id
54
+ "#{@collection.log_id} > #{protocol} #{port_range}"
55
+ end
56
+
57
+ def diff(dsl)
58
+ dsl_ip_ranges, dsl_groups, self_ip_ranges, self_groups = normalize_attrs(dsl)
59
+
60
+ [
61
+ dsl_ip_ranges - self_ip_ranges,
62
+ self_ip_ranges - dsl_ip_ranges,
63
+ dsl_groups - self_groups,
64
+ self_groups - dsl_groups,
65
+ ]
66
+ end
67
+
68
+ def normalize_attrs(dsl)
69
+ dsl_ip_ranges = (dsl.ip_ranges || []).sort
70
+ dsl_groups = (dsl.groups || []).map {|i|
71
+ i.kind_of?(Array) ? i : [@options.ec2.owner_id, i]
72
+ }.sort
73
+
74
+ self_ip_ranges, self_groups = normalize_self_attrs
75
+
76
+ [dsl_ip_ranges, dsl_groups, self_ip_ranges, self_groups]
77
+ end
78
+
79
+ def normalize_self_attrs
80
+ self_ip_ranges = (@permission.ip_ranges || []).sort
81
+ self_groups = (@permission.groups || []).map {|i|
82
+ [i.owner_id, i.name]
83
+ }.sort
84
+
85
+ [self_ip_ranges, self_groups]
86
+ end
87
+ end # Permission
88
+ end # PermissionCollection
89
+ end # SecurityGroup
90
+ end # SecurityGroupCollection
91
+ end # EC2Wrapper
92
+ end # Piculet
@@ -0,0 +1,36 @@
1
+ require 'ostruct'
2
+ require 'piculet/logger'
3
+ require 'piculet/wrapper/security-group'
4
+
5
+ module Piculet
6
+ class EC2Wrapper
7
+ class SecurityGroupCollection
8
+ include Logger::ClientHelper
9
+
10
+ def initialize(security_groups, options)
11
+ @security_groups = security_groups
12
+ @options = options
13
+ end
14
+
15
+ def each
16
+ @security_groups.each do |sg|
17
+ yield(SecurityGroup.new(sg, @options))
18
+ end
19
+ end
20
+
21
+ def create(name, opts = {})
22
+ log(:info, 'Create SecurityGroup', :cyan, "#{opts[:vpc] || :classic} > #{name}")
23
+ log(:warn, '`egress any 0.0.0.0/0` is implicitly defined', :yellow) if @options.dry_run && opts[:vpc]
24
+
25
+ if @options.dry_run
26
+ sg = OpenStruct.new({:id => '<new security group>', :name => name}.merge(opts))
27
+ else
28
+ sg = @security_groups.create(name, opts)
29
+ @options.updated = true
30
+ end
31
+
32
+ SecurityGroup.new(sg, @options)
33
+ end
34
+ end # SecurityGroupCollection
35
+ end # EC2Wrapper
36
+ end # Piculet
@@ -0,0 +1,56 @@
1
+ require 'forwardable'
2
+ require 'piculet/logger'
3
+ require 'piculet/wrapper/permission-collection'
4
+
5
+ module Piculet
6
+ class EC2Wrapper
7
+ class SecurityGroupCollection
8
+ class SecurityGroup
9
+ extend Forwardable
10
+ include Logger::ClientHelper
11
+
12
+ def_delegators(
13
+ :@security_group,
14
+ :vpc_id, :name, :vpc?)
15
+
16
+ def initialize(security_group, options)
17
+ @security_group = security_group
18
+ @options = options
19
+ end
20
+
21
+ def eql?(dsl)
22
+ @security_group.description == dsl.description
23
+ end
24
+
25
+ def update(dsl)
26
+ if @security_group.description != dsl.description
27
+ log(:warn, '`description` cannot be updated', :yellow, "#{vpc_id || :classic} > #{name}")
28
+ end
29
+
30
+ # XXX:
31
+ end
32
+
33
+ def delete
34
+ log(:info, 'Delete SecurityGroup', :red, "#{vpc_id || :classic} > #{name}")
35
+
36
+ if name == 'default'
37
+ log(:warn, 'SecurityGroup `default` is reserved', :yellow)
38
+ else
39
+ unless @options.dry_run
40
+ @security_group.delete
41
+ @options.updated = true
42
+ end
43
+ end
44
+ end
45
+
46
+ def ingress_ip_permissions
47
+ PermissionCollection.new(@security_group, :ingress, @options)
48
+ end
49
+
50
+ def egress_ip_permissions
51
+ PermissionCollection.new(@security_group, :egress, @options)
52
+ end
53
+ end # SecurityGroup
54
+ end # SecurityGroupCollection
55
+ end # EC2Wrapper
56
+ end # Piculet
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: piculet
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - winebarrel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.19.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.19.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: term-ansicolor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.2.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Piculet is a tool to manage Security Group. It defines the state of Security
95
+ Group using DSL, and updates Security Group according to DSL.
96
+ email:
97
+ - sgwr_dts@yahoo.co.jp
98
+ executables:
99
+ - piculet
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - README.md
104
+ - bin/piculet
105
+ - lib/piculet/client.rb
106
+ - lib/piculet/dsl/converter.rb
107
+ - lib/piculet/dsl/ec2.rb
108
+ - lib/piculet/dsl/permission.rb
109
+ - lib/piculet/dsl/permissions.rb
110
+ - lib/piculet/dsl/security-group.rb
111
+ - lib/piculet/dsl.rb
112
+ - lib/piculet/exporter.rb
113
+ - lib/piculet/ext/ec2-owner-id-ext.rb
114
+ - lib/piculet/ext/ip-permission-collection-ext.rb
115
+ - lib/piculet/ext/string-ext.rb
116
+ - lib/piculet/logger.rb
117
+ - lib/piculet/version.rb
118
+ - lib/piculet/wrapper/ec2-wrapper.rb
119
+ - lib/piculet/wrapper/permission-collection.rb
120
+ - lib/piculet/wrapper/permission.rb
121
+ - lib/piculet/wrapper/security-group-collection.rb
122
+ - lib/piculet/wrapper/security-group.rb
123
+ - lib/piculet.rb
124
+ homepage: https://bitbucket.org/winebarrel/piculet
125
+ licenses:
126
+ - MIT
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 1.8.23
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: Piculet is a tool to manage Security Group.
149
+ test_files: []