xlsx_to_k8s_network_policy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ac9cb890b2d2eba2e4b42ceaab0fa0694151b77c8d47315c7215ed1c72d6f581
4
+ data.tar.gz: dfb6e10fd68f634edfee4a100d6d2876c809d6bd915a49907cd59de05a84f374
5
+ SHA512:
6
+ metadata.gz: 9f6e8d9ed965d98a13a9b07a9c0734f1df2553ccb8b976d2fae32d642b629f482b2969bf99c23dd1d10044a5265de0c8ca464ffe5f86c612ac714c7326ef461c
7
+ data.tar.gz: 57e1222d17672a34618533611ab0de5e4bffd59e791ad2137a26af4cdac4b2d6e65acc6af4bf0ec707860da7aacbe918f9b6bfac3b64d8df166a2ac96722ee03
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'activesupport', '~> 5.1.4'
6
+ gem 'roo', '~> 2.7.1'
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem 'bundler', '~> 1.16.1'
12
+ gem 'juwelier', '~> 2.1.0'
13
+ gem 'rdoc', '~> 6.0.1'
14
+ gem 'rspec', '~> 3.7'
15
+ gem 'rubocop', '~> 0.52.1'
16
+ gem 'shoulda', '~> 3.5.0'
17
+ gem 'simplecov', '~> 0.15.1'
18
+ end
@@ -0,0 +1,122 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activesupport (5.1.4)
5
+ concurrent-ruby (~> 1.0, >= 1.0.2)
6
+ i18n (~> 0.7)
7
+ minitest (~> 5.1)
8
+ tzinfo (~> 1.1)
9
+ addressable (2.5.2)
10
+ public_suffix (>= 2.0.2, < 4.0)
11
+ ast (2.4.0)
12
+ builder (3.2.3)
13
+ concurrent-ruby (1.0.5)
14
+ descendants_tracker (0.0.4)
15
+ thread_safe (~> 0.3, >= 0.3.1)
16
+ diff-lcs (1.3)
17
+ docile (1.1.5)
18
+ faraday (0.12.2)
19
+ multipart-post (>= 1.2, < 3)
20
+ git (1.3.0)
21
+ github_api (0.18.2)
22
+ addressable (~> 2.4)
23
+ descendants_tracker (~> 0.0.4)
24
+ faraday (~> 0.8)
25
+ hashie (~> 3.5, >= 3.5.2)
26
+ oauth2 (~> 1.0)
27
+ hashie (3.5.7)
28
+ highline (1.7.10)
29
+ i18n (0.9.3)
30
+ concurrent-ruby (~> 1.0)
31
+ json (2.1.0)
32
+ juwelier (2.1.3)
33
+ builder
34
+ bundler (>= 1.13)
35
+ git (>= 1.2.5)
36
+ github_api
37
+ highline (>= 1.6.15)
38
+ nokogiri (>= 1.5.10)
39
+ rake
40
+ rdoc
41
+ semver
42
+ jwt (1.5.6)
43
+ mini_portile2 (2.3.0)
44
+ minitest (5.11.3)
45
+ multi_json (1.13.1)
46
+ multi_xml (0.6.0)
47
+ multipart-post (2.0.0)
48
+ nokogiri (1.8.2)
49
+ mini_portile2 (~> 2.3.0)
50
+ oauth2 (1.4.0)
51
+ faraday (>= 0.8, < 0.13)
52
+ jwt (~> 1.0)
53
+ multi_json (~> 1.3)
54
+ multi_xml (~> 0.5)
55
+ rack (>= 1.2, < 3)
56
+ parallel (1.12.1)
57
+ parser (2.4.0.2)
58
+ ast (~> 2.3)
59
+ powerpack (0.1.1)
60
+ public_suffix (3.0.1)
61
+ rack (2.0.4)
62
+ rainbow (3.0.0)
63
+ rake (12.3.0)
64
+ rdoc (6.0.1)
65
+ roo (2.7.1)
66
+ nokogiri (~> 1)
67
+ rubyzip (~> 1.1, < 2.0.0)
68
+ rspec (3.7.0)
69
+ rspec-core (~> 3.7.0)
70
+ rspec-expectations (~> 3.7.0)
71
+ rspec-mocks (~> 3.7.0)
72
+ rspec-core (3.7.1)
73
+ rspec-support (~> 3.7.0)
74
+ rspec-expectations (3.7.0)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.7.0)
77
+ rspec-mocks (3.7.0)
78
+ diff-lcs (>= 1.2.0, < 2.0)
79
+ rspec-support (~> 3.7.0)
80
+ rspec-support (3.7.1)
81
+ rubocop (0.52.1)
82
+ parallel (~> 1.10)
83
+ parser (>= 2.4.0.2, < 3.0)
84
+ powerpack (~> 0.1)
85
+ rainbow (>= 2.2.2, < 4.0)
86
+ ruby-progressbar (~> 1.7)
87
+ unicode-display_width (~> 1.0, >= 1.0.1)
88
+ ruby-progressbar (1.9.0)
89
+ rubyzip (1.2.1)
90
+ semver (1.0.1)
91
+ shoulda (3.5.0)
92
+ shoulda-context (~> 1.0, >= 1.0.1)
93
+ shoulda-matchers (>= 1.4.1, < 3.0)
94
+ shoulda-context (1.2.2)
95
+ shoulda-matchers (2.8.0)
96
+ activesupport (>= 3.0.0)
97
+ simplecov (0.15.1)
98
+ docile (~> 1.1.0)
99
+ json (>= 1.8, < 3)
100
+ simplecov-html (~> 0.10.0)
101
+ simplecov-html (0.10.2)
102
+ thread_safe (0.3.6)
103
+ tzinfo (1.2.5)
104
+ thread_safe (~> 0.1)
105
+ unicode-display_width (1.3.0)
106
+
107
+ PLATFORMS
108
+ ruby
109
+
110
+ DEPENDENCIES
111
+ activesupport (~> 5.1.4)
112
+ bundler (~> 1.16.1)
113
+ juwelier (~> 2.1.0)
114
+ rdoc (~> 6.0.1)
115
+ roo (~> 2.7.1)
116
+ rspec (~> 3.7)
117
+ rubocop (~> 0.52.1)
118
+ shoulda (~> 3.5.0)
119
+ simplecov (~> 0.15.1)
120
+
121
+ BUNDLED WITH
122
+ 1.16.1
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018 Alistair A. Israel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,165 @@
1
+ # xlsx_to_k8s_network_policy
2
+
3
+ Converts an Excel (`.xlsx`) spreadsheet into a Kubernetes network policy resource definition YAML file.
4
+
5
+ See [https://kubernetes.io/docs/concepts/services-networking/network-policies/](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
6
+
7
+ See test/fixtures/network_policy.xlsx, or [this Google sheet](https://docs.google.com/spreadsheets/d/e/2PACX-1vRj2xVTUJERb9oP9rBth1hbAef5XwXO5NrBUIK1HbryBFMhrE7J5YtXiWNUuxEnb3oB7kcJBKDWoIT2/pubhtml) for a sample Excel file.
8
+
9
+ #### Sample Network Policy
10
+
11
+ First, define a `Zones` sheet that contains the zones and their corresponding network CIDRs. Separate multiple CIDRs using commas. For example:
12
+
13
+ |Zone |CIDRs |
14
+ |--------------|--------------------------|
15
+ |Front End |10.10.1.0/24, 10.10.2.0/24|
16
+ |Back End |10.11.0.0/24 |
17
+ |Infrastructure|10.12.0.0/24 |
18
+
19
+ Next, define a `ZoneToZone` sheet that defines the zone to zone network access. For example:
20
+
21
+ | |Front End|Back End|Infrastructure|
22
+ |--------------|---------|--------|--------------|
23
+ |Front End |Y |Y |N |
24
+ |Back End | |Y |Y |
25
+ |Infrastructure| | |Y |
26
+
27
+ This defines rules that allow intra-zone traffic for all zones, and one-way traffic from the `Front End` zone to the `Back End` zone, and from the `Back End` zone to the `Infrastructure` zone.
28
+
29
+
30
+ #### Generated YAML
31
+
32
+ That Excel file generates the following YAML file:
33
+
34
+ ```
35
+ ---
36
+ apiVersion: networking.k8s.io/v1
37
+ kind: NetworkPolicy
38
+ metadata:
39
+ name: default-deny
40
+ spec:
41
+ podSelector: {}
42
+ policyTypes:
43
+ - Ingress
44
+ - Egress
45
+ ---
46
+ apiVersion: networking.k8s.io/v1
47
+ kind: NetworkPolicy
48
+ metadata:
49
+ name: front-end-zone
50
+ spec:
51
+ podSelector:
52
+ matchLabels:
53
+ zone: front-end
54
+ policyTypes:
55
+ - Ingress
56
+ - Egress
57
+ ingress:
58
+ - from:
59
+ - podSelector:
60
+ matchLabels:
61
+ zone: front-end
62
+ - ipBlock: 10.10.1.0/24
63
+ - ipBlock: 10.10.2.0/24
64
+ egress:
65
+ - to:
66
+ - podSelector:
67
+ matchLabels:
68
+ zone: front-end
69
+ - ipBlock: 10.10.1.0/24
70
+ - ipBlock: 10.10.2.0/24
71
+ - podSelector:
72
+ matchLabels:
73
+ zone: back-end
74
+ - ipBlock: 10.11.0.0/24
75
+ ---
76
+ apiVersion: networking.k8s.io/v1
77
+ kind: NetworkPolicy
78
+ metadata:
79
+ name: back-end-zone
80
+ spec:
81
+ podSelector:
82
+ matchLabels:
83
+ zone: back-end
84
+ policyTypes:
85
+ - Ingress
86
+ - Egress
87
+ ingress:
88
+ - from:
89
+ - podSelector:
90
+ matchLabels:
91
+ zone: back-end
92
+ - ipBlock: 10.11.0.0/24
93
+ - podSelector:
94
+ matchLabels:
95
+ zone: front-end
96
+ - ipBlock: 10.10.1.0/24
97
+ - ipBlock: 10.10.2.0/24
98
+ egress:
99
+ - to:
100
+ - podSelector:
101
+ matchLabels:
102
+ zone: back-end
103
+ - ipBlock: 10.11.0.0/24
104
+ - podSelector:
105
+ matchLabels:
106
+ zone: infrastructure
107
+ - ipBlock: 10.12.0.0/24
108
+ ---
109
+ apiVersion: networking.k8s.io/v1
110
+ kind: NetworkPolicy
111
+ metadata:
112
+ name: infrastructure-zone
113
+ spec:
114
+ podSelector:
115
+ matchLabels:
116
+ zone: infrastructure
117
+ policyTypes:
118
+ - Ingress
119
+ - Egress
120
+ ingress:
121
+ - from:
122
+ - podSelector:
123
+ matchLabels:
124
+ zone: infrastructure
125
+ - ipBlock: 10.12.0.0/24
126
+ - podSelector:
127
+ matchLabels:
128
+ zone: back-end
129
+ - ipBlock: 10.11.0.0/24
130
+ egress:
131
+ - to:
132
+ - podSelector:
133
+ matchLabels:
134
+ zone: infrastructure
135
+ - ipBlock: 10.12.0.0/24
136
+ ```
137
+
138
+ ### Installation
139
+
140
+ This gem was developed using Ruby 2.5.0, but may work with earlier Ruby 2.x.
141
+
142
+ ```
143
+ $ gem install xlsx_to_k8s_network_policy
144
+ ```
145
+
146
+ ### Usage
147
+
148
+ ```
149
+ $ xlsx_to_k8s_network_policy network_policy.xlsx network_policy.yml
150
+ ```
151
+
152
+ #### Contributing to `xlsx_to_k8s_network_policy`
153
+
154
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
155
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
156
+ * Fork the project.
157
+ * Start a feature/bugfix branch.
158
+ * Commit and push until you are happy with your contribution.
159
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
160
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
161
+
162
+ #### Copyright
163
+
164
+ Copyright (c) 2018 Alistair A. Israel. See LICENSE.txt for
165
+ further details.
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ warn e.message
9
+ warn 'Run `bundle install` to install missing gems'
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+ require 'juwelier'
14
+ Juwelier::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
16
+ gem.name = 'xlsx_to_k8s_network_policy'
17
+ gem.homepage = 'http://github.com/aisrael/xlsx_to_k8s_network_policy'
18
+ gem.license = 'MIT'
19
+ gem.summary = %(Generate Kubernetes Network Policy from Excel)
20
+ gem.description = %(Generate Kubernetes Network Policy YAML resource definitions from .xlsx Excel spreadsheets)
21
+ gem.email = 'aisrael@gmail.com'
22
+ gem.authors = ['Alistair A. Israel']
23
+ gem.files.exclude '.*'
24
+ gem.files.exclude 'test/**/*'
25
+ gem.files.exclude 'spec/**/*'
26
+
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Juwelier::RubygemsDotOrgTasks.new
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ desc 'Code coverage detail'
38
+ task :simplecov do
39
+ ENV['COVERAGE'] = 'true'
40
+ Rake::Task['test'].execute
41
+ end
42
+
43
+ task default: :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ''
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "xlsx_to_k8s_network_policy #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'xlsx_to_k8s_network_policy'
5
+
6
+ if ARGV.size < 2
7
+ puts <<-USAGE
8
+ Usage:
9
+
10
+ xls_to_k8s_network_policy <path/to/document.xlsx> <path/to/output.yml>
11
+ USAGE
12
+ exit 1
13
+ end
14
+
15
+ XLSX_FILENAME = ARGV[0]
16
+ unless File.exist?(XLSX_FILENAME) && File.readable?(XLSX_FILENAME)
17
+ warn %(File "#{XLSX_FILENAME}" does not exist or cannot be read!)
18
+ exit 1
19
+ end
20
+
21
+ unless File.file?(XLSX_FILENAME)
22
+ warn %(File "#{XLSX_FILENAME}" is not a regular file!)
23
+ exit 1
24
+ end
25
+
26
+ OUTPUT_FILENAME = ARGV[1]
27
+ OUTPUT_DIR = File.dirname(OUTPUT_FILENAME)
28
+
29
+ unless File.exist?(OUTPUT_DIR) &&
30
+ File.directory?(OUTPUT_DIR) &&
31
+ File.readable?(OUTPUT_DIR) &&
32
+ File.writable?(OUTPUT_DIR)
33
+ warn %(Directory "#{OUTPUT_DIR}" does not exist or cannot be read or written to!)
34
+ exit 1
35
+ end
36
+
37
+ if File.exist?(OUTPUT_FILENAME)
38
+ warn %(File "#{OUTPUT_FILENAME}" exists! Cowardly refusing to clobber output file.)
39
+ exit 1
40
+ end
41
+
42
+ network_policies = Reader.read(XLSX_FILENAME)
43
+ Writer.write(network_policies, OUTPUT_FILENAME)
@@ -0,0 +1,365 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/hash'
5
+ require 'roo'
6
+ require 'yaml'
7
+
8
+ # A base label selector
9
+ class LabelSelector
10
+ # A `matchLabels` label selector
11
+ class MatchLabels < LabelSelector
12
+ attr_reader :labels
13
+ def initialize(labels = nil)
14
+ @labels = labels || {}
15
+ end
16
+
17
+ def []=(key, value)
18
+ @labels[key] = value
19
+ end
20
+
21
+ def as_hash
22
+ return {} if @labels.empty?
23
+ {
24
+ matchLabels: @labels
25
+ }
26
+ end
27
+ end
28
+ # TODO: MatchExpressions < LabelSelector
29
+ def ==(other)
30
+ other.class == self.class && other.labels == labels
31
+ end
32
+ end
33
+
34
+ # A `podSelector`
35
+ class PodSelector
36
+ attr_reader :label_selector
37
+
38
+ def initialize(label_selector = nil)
39
+ @label_selector = label_selector || LabelSelector::MatchLabels.new
40
+ end
41
+
42
+ def []=(key, value)
43
+ @label_selector[key] = value
44
+ end
45
+
46
+ def as_hash
47
+ {
48
+ podSelector: label_selector.as_hash
49
+ }
50
+ end
51
+
52
+ def ==(other)
53
+ other.class == self.class && other.label_selector == label_selector
54
+ end
55
+ end
56
+
57
+ # The _real_ NetworkPolicy
58
+ class NetworkPolicy
59
+ attr_reader :name
60
+ attr_reader :pod_selector
61
+ attr_reader :ingresses
62
+ attr_reader :egresses
63
+
64
+ def self.deny_all
65
+ NetworkPolicy.new('default-deny', PodSelector.new)
66
+ end
67
+
68
+ def initialize(name, pod_selector)
69
+ raise %(Invalid name "#{name}". Must consist of [a-z_-]+) unless /^[a-z_-]+$/ =~ name
70
+ @name = name
71
+ @pod_selector = pod_selector
72
+ @ingresses = []
73
+ @egresses = []
74
+ end
75
+
76
+ class NetworkPolicyPeer
77
+ # An {ipBlock: cidr} NetworkPolicyPeer
78
+ class IPBlock < NetworkPolicyPeer
79
+ attr_reader :cidr
80
+ def initialize(cidr = nil)
81
+ @cidr = cidr || []
82
+ end
83
+
84
+ def as_hash
85
+ {
86
+ ipBlock: cidr
87
+ }
88
+ end
89
+
90
+ def ==(other)
91
+ other.class == self.class && other.cidr == cidr
92
+ end
93
+ end
94
+
95
+ # A {podSelector: {...}} NetworkPolicyPeer
96
+ class PodSelectorNPP < NetworkPolicyPeer
97
+ attr_reader :pod_selector
98
+ def initialize(pod_selector)
99
+ @pod_selector = pod_selector
100
+ end
101
+ delegate :as_hash, to: :pod_selector
102
+ def ==(other)
103
+ other.class == self.class && other.pod_selector == pod_selector
104
+ end
105
+ end
106
+
107
+ # A {namespaceSelector: {...}} NetworkPolicyPeer
108
+ class NamespaceSelector < NetworkPolicyPeer
109
+ attr_reader :label_selector
110
+ def initialize(label_selector = nil)
111
+ @label_selector = label_selector || LabelSelector::MatchLabels.new
112
+ end
113
+
114
+ def []=(key, value)
115
+ @label_selector[key] = value
116
+ end
117
+
118
+ def as_hash
119
+ {
120
+ labelSelector: @label_selector.as_hash
121
+ }
122
+ end
123
+
124
+ def ==(other)
125
+ other.class == self.class && other.label_selector == label_selector
126
+ end
127
+ end
128
+ end
129
+
130
+ def add_pod_selector_ingress(pod_selector)
131
+ add_ingress(NetworkPolicyPeer::PodSelectorNPP.new(pod_selector))
132
+ end
133
+
134
+ def add_pod_selector_egress(pod_selector)
135
+ add_egress(NetworkPolicyPeer::PodSelectorNPP.new(pod_selector))
136
+ end
137
+
138
+ def add_cidr_ingress(cidr)
139
+ add_ingress(NetworkPolicyPeer::IPBlock.new(cidr))
140
+ end
141
+
142
+ def add_cidr_egress(cidr)
143
+ add_egress(NetworkPolicyPeer::IPBlock.new(cidr))
144
+ end
145
+
146
+ def add_ingress(ingress)
147
+ npp = case ingress
148
+ when PodSelector
149
+ NetworkPolicyPeer::PodSelectorNPP.new(ingress)
150
+ when NetworkPolicyPeer
151
+ ingress
152
+ else
153
+ raise "Don't know how to handle ingress of type #{ingress.class}!"
154
+ end
155
+ @ingresses << npp unless @ingresses.include?(npp)
156
+ end
157
+
158
+ def add_egress(egress)
159
+ npp = case egress
160
+ when PodSelector
161
+ NetworkPolicyPeer::PodSelectorNPP.new(egress)
162
+ when NetworkPolicyPeer
163
+ egress
164
+ else
165
+ raise "Don't know how to handle ingress of type #{egress.class}!"
166
+ end
167
+ @egresses << npp unless @egresses.include?(npp)
168
+ end
169
+
170
+ def as_hash
171
+ policy_types = []
172
+ policy_types << 'Ingress' if !@ingresses.empty? || @egresses.empty?
173
+ policy_types << 'Egress' if !@egresses.empty? || @ingresses.empty?
174
+ spec = pod_selector.as_hash
175
+ spec[:policyTypes] = policy_types
176
+ hash = {
177
+ apiVersion: 'networking.k8s.io/v1',
178
+ kind: 'NetworkPolicy',
179
+ metadata: {
180
+ name: name
181
+ },
182
+ spec: spec
183
+ }
184
+ add_ingress_and_egress(hash)
185
+ hash.deep_stringify_keys
186
+ end
187
+
188
+ private
189
+
190
+ def add_ingress_and_egress(hash)
191
+ unless @ingresses.empty?
192
+ hash[:spec][:ingress] = [
193
+ {
194
+ from: @ingresses.map(&:as_hash)
195
+ }
196
+ ]
197
+ end
198
+ return if @egresses.empty?
199
+ hash[:spec][:egress] = [
200
+ {
201
+ to: @egresses.map(&:as_hash)
202
+ }
203
+ ]
204
+ end
205
+ end
206
+
207
+ # A collection of `NetworkPolicy`s
208
+ class NetworkPolicies
209
+ attr_reader :zones
210
+ attr_reader :policies
211
+
212
+ def initialize
213
+ @zones = {}
214
+ @policies = {}
215
+ end
216
+
217
+ def zone_names
218
+ @zones.values.map(&:name)
219
+ end
220
+
221
+ def add_zone(name, cidrs)
222
+ zone = Zone.new(name, cidrs)
223
+ @zones[name] = zone
224
+ @policies[zone] = zone.to_network_policy
225
+ end
226
+
227
+ def allow(from_zone_name, to_zone_name)
228
+ from_zone = @zones[from_zone_name]
229
+ raise "No zone named #{from_zone_name}!" unless from_zone
230
+ to_zone = @zones[to_zone_name]
231
+ raise "No zone named #{to_zone_name}!" unless to_zone
232
+ from_zone.add_ingress_rules_to(policies[to_zone])
233
+ to_zone.add_egress_rules_to(policies[from_zone])
234
+ end
235
+
236
+ def to_doc_hashes
237
+ docs = [NetworkPolicy.deny_all] + @policies.values
238
+ docs.map(&:as_hash)
239
+ end
240
+
241
+ # A Zone
242
+ class Zone
243
+ attr_reader :name, :cidrs
244
+ def initialize(name, cidrs)
245
+ @name = name
246
+ @cidrs = cidrs
247
+ end
248
+
249
+ def normalized_name
250
+ name.parameterize
251
+ end
252
+
253
+ def to_pod_selector
254
+ @pod_selector ||= begin
255
+ label_selector = LabelSelector::MatchLabels.new(zone: normalized_name)
256
+ PodSelector.new(label_selector)
257
+ end
258
+ end
259
+
260
+ def to_network_policy
261
+ np = NetworkPolicy.new("#{normalized_name}-zone", to_pod_selector)
262
+ add_ingress_rules_to(np)
263
+ add_egress_rules_to(np)
264
+ np
265
+ end
266
+
267
+ def add_ingress_rules_to(np)
268
+ np.add_pod_selector_ingress(to_pod_selector)
269
+ @cidrs.each do |cidr|
270
+ np.add_cidr_ingress(cidr)
271
+ end
272
+ end
273
+
274
+ def add_egress_rules_to(np)
275
+ np.add_pod_selector_egress(to_pod_selector)
276
+ @cidrs.each do |cidr|
277
+ np.add_cidr_egress(cidr)
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ # Reads an XLSX file and creates the NetworkPolicy
284
+ class Reader
285
+ attr_reader :file
286
+
287
+ def self.read(file)
288
+ Reader.new(file).read
289
+ end
290
+
291
+ def initialize(file)
292
+ @file = file
293
+ end
294
+
295
+ def read
296
+ @xlsx = Roo::Spreadsheet.open('./test/fixtures/network_policy.xlsx')
297
+ @network_policy = NetworkPolicies.new
298
+ read_zones
299
+ read_rules
300
+ @network_policy
301
+ end
302
+
303
+ private
304
+
305
+ def read_zones
306
+ zones_sheet = @xlsx.sheet('Zones')
307
+ zones_sheet.each(name: 'Zone', cidrs: 'CIDRs') do |h|
308
+ next if h[:name] == 'Zone' && h[:cidrs] == 'CIDRs'
309
+ @network_policy.add_zone(h[:name], h[:cidrs].split(/\s*,\s*/))
310
+ end
311
+ end
312
+
313
+ def read_rules
314
+ rules_sheet = @xlsx.sheet_for('ZoneToZone')
315
+ column_zones = extract_column_zones(rules_sheet)
316
+ extract_rules(rules_sheet, column_zones)
317
+ end
318
+
319
+ def extract_rules(rules_sheet, column_zones)
320
+ rules_sheet.each_row(offset: 1) do |row|
321
+ from_zone = row[0].value
322
+ create_rules_from_row(column_zones, row, from_zone)
323
+ end
324
+ end
325
+
326
+ def create_rules_from_row(column_zones, row, from_zone)
327
+ column_zones.size.times do |i|
328
+ target = row.find do |cell|
329
+ cell.coordinate.column == i + 2
330
+ end
331
+ if target && target.value == 'Y'
332
+ to_zone = column_zones[i]
333
+ @network_policy.allow(from_zone, to_zone)
334
+ end
335
+ end
336
+ end
337
+
338
+ def extract_column_zones(rules_sheet)
339
+ first_row = rules_sheet.row(rules_sheet.first_row)
340
+ column_zones = first_row[1..-1]
341
+ column_zones.each do |to_name|
342
+ raise %(To zone "#{from_zone}" not found in zones) unless @network_policy.zone_names.include?(to_name)
343
+ end
344
+ column_zones
345
+ end
346
+ end
347
+
348
+ # Writes a NetworkPolicy to YAML
349
+ class Writer
350
+ attr_reader :filename
351
+
352
+ def initialize(filename)
353
+ @filename = filename
354
+ end
355
+
356
+ def self.write(network_policy, filename)
357
+ Writer.new(filename).write(network_policy)
358
+ end
359
+
360
+ def write(network_policy)
361
+ File.open(filename, 'w') do |f|
362
+ f.write YAML.dump_stream(*network_policy.to_doc_hashes)
363
+ end
364
+ end
365
+ end
@@ -0,0 +1,74 @@
1
+ # Generated by juwelier
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: xlsx_to_k8s_network_policy 0.1.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "xlsx_to_k8s_network_policy".freeze
9
+ s.version = "0.1.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Alistair A. Israel".freeze]
14
+ s.date = "2018-02-08"
15
+ s.description = "Generate Kubernetes Network Policy YAML resource definitions from .xlsx Excel spreadsheets".freeze
16
+ s.email = "aisrael@gmail.com".freeze
17
+ s.executables = ["xlsx_to_k8s_network_policy".freeze]
18
+ s.extra_rdoc_files = [
19
+ "LICENSE.txt",
20
+ "README.md"
21
+ ]
22
+ s.files = [
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/xlsx_to_k8s_network_policy",
30
+ "lib/xlsx_to_k8s_network_policy.rb",
31
+ "xlsx_to_k8s_network_policy.gemspec"
32
+ ]
33
+ s.homepage = "http://github.com/aisrael/xlsx_to_k8s_network_policy".freeze
34
+ s.licenses = ["MIT".freeze]
35
+ s.rubygems_version = "2.7.3".freeze
36
+ s.summary = "Generate Kubernetes Network Policy from Excel".freeze
37
+
38
+ if s.respond_to? :specification_version then
39
+ s.specification_version = 4
40
+
41
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
42
+ s.add_runtime_dependency(%q<activesupport>.freeze, ["~> 5.1.4"])
43
+ s.add_runtime_dependency(%q<roo>.freeze, ["~> 2.7.1"])
44
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 1.16.1"])
45
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
46
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 6.0.1"])
47
+ s.add_development_dependency(%q<rspec>.freeze, ["~> 3.7"])
48
+ s.add_development_dependency(%q<rubocop>.freeze, ["~> 0.52.1"])
49
+ s.add_development_dependency(%q<shoulda>.freeze, ["~> 3.5.0"])
50
+ s.add_development_dependency(%q<simplecov>.freeze, ["~> 0.15.1"])
51
+ else
52
+ s.add_dependency(%q<activesupport>.freeze, ["~> 5.1.4"])
53
+ s.add_dependency(%q<roo>.freeze, ["~> 2.7.1"])
54
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.16.1"])
55
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
56
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.0.1"])
57
+ s.add_dependency(%q<rspec>.freeze, ["~> 3.7"])
58
+ s.add_dependency(%q<rubocop>.freeze, ["~> 0.52.1"])
59
+ s.add_dependency(%q<shoulda>.freeze, ["~> 3.5.0"])
60
+ s.add_dependency(%q<simplecov>.freeze, ["~> 0.15.1"])
61
+ end
62
+ else
63
+ s.add_dependency(%q<activesupport>.freeze, ["~> 5.1.4"])
64
+ s.add_dependency(%q<roo>.freeze, ["~> 2.7.1"])
65
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.16.1"])
66
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
67
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.0.1"])
68
+ s.add_dependency(%q<rspec>.freeze, ["~> 3.7"])
69
+ s.add_dependency(%q<rubocop>.freeze, ["~> 0.52.1"])
70
+ s.add_dependency(%q<shoulda>.freeze, ["~> 3.5.0"])
71
+ s.add_dependency(%q<simplecov>.freeze, ["~> 0.15.1"])
72
+ end
73
+ end
74
+
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xlsx_to_k8s_network_policy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alistair A. Israel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: roo
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.7.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.7.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.16.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.16.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: juwelier
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.1.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.1.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdoc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 6.0.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 6.0.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.52.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.52.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: shoulda
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 3.5.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 3.5.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.15.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.15.1
139
+ description: Generate Kubernetes Network Policy YAML resource definitions from .xlsx
140
+ Excel spreadsheets
141
+ email: aisrael@gmail.com
142
+ executables:
143
+ - xlsx_to_k8s_network_policy
144
+ extensions: []
145
+ extra_rdoc_files:
146
+ - LICENSE.txt
147
+ - README.md
148
+ files:
149
+ - Gemfile
150
+ - Gemfile.lock
151
+ - LICENSE.txt
152
+ - README.md
153
+ - Rakefile
154
+ - VERSION
155
+ - bin/xlsx_to_k8s_network_policy
156
+ - lib/xlsx_to_k8s_network_policy.rb
157
+ - xlsx_to_k8s_network_policy.gemspec
158
+ homepage: http://github.com/aisrael/xlsx_to_k8s_network_policy
159
+ licenses:
160
+ - MIT
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 2.7.3
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: Generate Kubernetes Network Policy from Excel
182
+ test_files: []