gloo_ingress_adapter 0.1.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
+ SHA256:
3
+ metadata.gz: 97de21875d8934bcca64e1ab8e1b8462fc1f3db4a53f372c7d3aa0c711038bdc
4
+ data.tar.gz: c873372e2f8ee77d2be7c531d09f24cda4ed190b635a334123b677dc1e58902c
5
+ SHA512:
6
+ metadata.gz: c79aa9f16640a0c6a78b324e6e8bd6db916b3b9567fa3ad5b622793878d571cb0ecc017552d1452da5a6f788f44e16bf29cebb144bccb99d29476d6b397b5f63
7
+ data.tar.gz: 22dc8fa2050e06649ccd4787ebf14c32d70baad5bfd82cd8fb25092c20dd54471d4ea1b7811c7358c23b85a906c488274cea59733865834d902d94d0e7e59e8c
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # Project
2
+
3
+ Use [Gloo Edge][gloo] gateways with Kubernetes ingress resources. Gloo Edge supports ingress, however it requires you
4
+ to deploy a separate gateway for this. With this adapter, you can integrate your Kubernetes ingresses into an existing
5
+ gateway.
6
+
7
+ The adapter works by monitoring ingress resources and automatically creating a Gloo route table for each ingress. You
8
+ can then mount the created route tables automatically in your gateway.
9
+
10
+ [gloo]: https://docs.solo.io/gloo-edge/latest/
11
+
12
+ ## Install
13
+
14
+ Install a Gloo Edge gateway:
15
+
16
+ ```bash
17
+ kubectl create namespace gloo-system
18
+ helm install gloo gloo --namespace gloo-system --repo https://storage.googleapis.com/solo-public-helm \
19
+ --set gateway.readGatewaysFromAllNamespaces=true
20
+ ```
21
+
22
+ You can also have a look at the [Gloo documentation][gloo-install] for detailed installation instructions.
23
+
24
+ [gloo-install]: https://docs.solo.io/gloo-edge/master/installation/gateway/kubernetes/
25
+
26
+ Install the ingress adapter into namespace `gloo-system`:
27
+
28
+ ```bash
29
+ git checkout git@github.com:CaperWhite/gloo-ingress-adapter.git
30
+ cd gloo-ingress-adapter
31
+ helm --namespace gloo-system install gloo-ingress-adapter charts
32
+ ```
33
+
34
+ After this, the ingress adapter should be up and running, and monitoring ingress resources. By default it will create
35
+ route tables for ingress resources with ingress class name `gloo-route`.
36
+
37
+ ## Verify installation
38
+
39
+ Deploy a test service:
40
+
41
+ ```bash
42
+ kubectl create namespace ingress-test
43
+ kubectl --namespace ingress-test apply --filename examples/test.yaml
44
+ ```
45
+
46
+ Create an ingress resource for the test service:
47
+
48
+ ```bash
49
+ cat <<-INGRESS | kubectl apply --namespace ingress-test --filename -
50
+ apiVersion: networking.k8s.io/v1
51
+ kind: Ingress
52
+ metadata:
53
+ name: ingress-test
54
+ spec:
55
+ ingressClassName: gloo-route
56
+ rules:
57
+ - http:
58
+ paths:
59
+ - path: /
60
+ pathType: Prefix
61
+ backend:
62
+ service:
63
+ name: ingress-test
64
+ port:
65
+ number: 8080
66
+ INGRESS
67
+ ```
68
+
69
+ Verify a route table has been created:
70
+
71
+ ```bash
72
+ kubectl --namespace ingress-test get routetable ingress-test -o yaml
73
+ ```
74
+
75
+ Create a virtual service resource that automatically mounts the created route tables:
76
+
77
+ ```bash
78
+ cat <<-SERVICE | kubectl --namespace gloo-system apply --filename -
79
+ apiVersion: gateway.solo.io/v1
80
+ kind: VirtualService
81
+ metadata:
82
+ name: ingress-test
83
+ spec:
84
+ virtualHost:
85
+ domains:
86
+ - ingress-test.local
87
+ routes:
88
+ - matchers:
89
+ - prefix: "/"
90
+ delegateAction:
91
+ selector:
92
+ labels:
93
+ ingress.caperwhite.com/protocol: http
94
+ namespaces:
95
+ - ingress-test
96
+ SERVICE
97
+ ```
98
+
99
+ Make the gateway accessible locally:
100
+
101
+ ```bash
102
+ kubectl --namespace gloo-system port-forward service/gateway-proxy 8080:80
103
+ ```
104
+
105
+ Check if you can access the service:
106
+
107
+ ```bash
108
+ curl -H 'Host: ingress-test.local' http://127.0.0.1:8080
109
+ ```
110
+
111
+ should return
112
+
113
+ ```json
114
+ {"result":true}
115
+ ```
116
+
117
+ Delete the ingress:
118
+
119
+ ```bash
120
+ kubectl --namespace ingress-test delete ingress ingress-test
121
+ ```
122
+
123
+ Verify the route table is gone:
124
+
125
+ ```bash
126
+ kubectl --namespace ingress-test get routetable -A
127
+ ```
128
+
129
+ ## Acknowledgements
130
+
131
+ "Gloo" is a trademark of [Solo.io, Inc.][solo].
132
+
133
+ [solo]: https://www.solo.io
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kubeclient"
4
+
5
+ module GlooIngressAdapter
6
+ # Factory for Kubernetes clients
7
+ class ClientFactory
8
+ KUBERNETES_API_URL = "https://kubernetes.default.svc"
9
+
10
+ def initialize(kubeconfig:, logger:)
11
+ @kubeconfig = kubeconfig.freeze
12
+ @logger = logger
13
+ end
14
+
15
+ def create_client(version:, api: nil)
16
+ suffix = api ? "/apis/#{api}" : ""
17
+
18
+ options = {
19
+ timeouts: { open: 30, read: 30 },
20
+ }
21
+
22
+ url = KUBERNETES_API_URL
23
+
24
+ if @kubeconfig
25
+ context = Kubeclient::Config.read(@kubeconfig).context
26
+
27
+ url = context.api_endpoint
28
+
29
+ options[:auth_options] = context.auth_options
30
+ options[:ssl_options] = context.ssl_options
31
+ else
32
+ options[:auth_options] = { bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" }
33
+
34
+ if File.exist?("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
35
+ options[:ssl_options] = { ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" }
36
+ end
37
+ end
38
+
39
+ Kubeclient::Client.new("#{url}#{suffix}", version, **options)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string"
4
+ require "gloo_ingress_adapter/resource_observer"
5
+ require "kubeclient"
6
+ require "logger"
7
+ require "retriable"
8
+ require "set"
9
+ require "yaml"
10
+
11
+ module GlooIngressAdapter
12
+ # Controller responsible for creating virtual services from ingress resources
13
+ class Controller
14
+ CONTROLLER_NAME = "caperwhite.com/gloo-ingress-adapter"
15
+
16
+ RETRY_ON = {
17
+ StandardError: nil,
18
+ }.freeze
19
+
20
+ def initialize(kubeconfig:, logger:, route_table_builder:)
21
+ @client_factory = ClientFactory.new(kubeconfig:, logger:).freeze
22
+ @logger = logger
23
+ @route_table_builder = route_table_builder
24
+ @ingress_classes = {}
25
+ @ingresses = {}
26
+ end
27
+
28
+ def run
29
+ logger.info("Running ingress adapter")
30
+ end
31
+
32
+ def watch
33
+ logger.info("Starting ingress adapter")
34
+
35
+ queue = Thread::Queue.new
36
+
37
+ ingress_class_observer = create_ingress_class_observer
38
+
39
+ ingress_class_observer.start(queue:)
40
+
41
+ ingress_observer = create_ingress_observer
42
+
43
+ ingress_observer.start(queue:)
44
+
45
+ loop do
46
+ event = queue.shift
47
+ handle_event(event)
48
+ end
49
+ rescue StandardError => e
50
+ log_exception(e)
51
+ end
52
+
53
+ protected
54
+
55
+ attr_reader :logger, :queue, :client_factory, :ingress_classes, :ingresses
56
+
57
+ def update_ingress_class(ingress_class)
58
+ logger.info("Updating ingress class #{ingress_class.metadata.name}")
59
+
60
+ existing_ingress_class = @ingress_classes[ingress_class.metadata.uid]
61
+
62
+ @ingress_classes[ingress_class.metadata.uid] = ingress_class
63
+
64
+ if @ingress_classes.empty?
65
+ logger.warn("Now handling no ingress classes")
66
+ else
67
+ logger.info("Now handling ingress classes #{@ingress_classes.values.map { |c| c.metadata.name }.join(", ")}")
68
+ end
69
+
70
+ controller_changed = existing_ingress_class.nil? ||
71
+ ingress_class.spec.controller != existing_ingress_class.spec.controller
72
+
73
+ ingress_class_name_changed = existing_ingress_class.nil? ||
74
+ existing_ingress_class.metadata.name != ingress_class.metadata.name
75
+
76
+ if controller_changed
77
+ if ingress_class.spec.controller == CONTROLLER_NAME
78
+ update_ingresses(ingress_class:)
79
+ elsif existing_ingress_class && existing_ingress_class.spec.controller == CONTROLLER_NAME
80
+ delete_ingresses(ingress_class:) if controller_changed
81
+ end
82
+ elsif ingress_class_name_changed
83
+ update_ingresses(ingress_class:)
84
+ end
85
+ end
86
+
87
+ def update_ingresses(ingress_class:)
88
+ @ingresses.values.filter do |u|
89
+ u.spec.ingressClassName == ingress_class.metadata.name
90
+ end.each do |ingress|
91
+ update_ingress(ingress)
92
+ end
93
+ end
94
+
95
+ def delete_ingressses(ingress_class:)
96
+ @ingresses.values.filter do |u|
97
+ u.spec.ingressClassName == ingress_class.metadata.name
98
+ end.each do |ingress|
99
+ networking_client.delete_ingress(ingress.metadata.name, ingress.metadata.namespace)
100
+ end
101
+ end
102
+
103
+ def remove_ingress_class(uid:)
104
+ ingress_class = @ingress_classes.delete(uid) || raise("Unknown ingress class uid '#{uid}'")
105
+
106
+ logger.info("Removing ingress class #{ingress_class.metadata.name}")
107
+
108
+ delete_ingresses(ingress_class:) if ingress_class.spec.controller == CONTROLLER_NAME
109
+
110
+ ingress_class
111
+ end
112
+
113
+ def update_ingress(ingress)
114
+ @ingresses[ingress.metadata.uid] = ingress
115
+
116
+ msg = <<~MSG.squish
117
+ #{ingress.metadata.name} (namespace: #{ingress.metadata.namespace},
118
+ ingressClass: #{ingress.spec.ingressClassName || "(none)"})
119
+ MSG
120
+
121
+ if ingress_class_names.include?(ingress.spec.ingressClassName)
122
+ logger.info("Updating ingress #{msg}")
123
+
124
+ update_route_table(ingress:)
125
+ else
126
+ logger.info("Ignoring ingress #{msg}")
127
+ end
128
+ end
129
+
130
+ def remove_ingress(uid:)
131
+ ingress = @ingress_classes.delete(uid) || raise("Unknown ingress class uid '#{uid}'")
132
+
133
+ logger.info("Removed ingress #{ingress.metadata.name} (namespace: #{ingress.metadata.namespace})")
134
+
135
+ ingress
136
+ end
137
+
138
+ def handle_event(event)
139
+ raise("Received error event: #{event.to_nice_yaml}") if event.type == "ERROR"
140
+
141
+ logger.info("Handling event: #{event.object.kind} #{event.object.metadata.name} #{event.type.downcase}")
142
+ logger.debug { "Event object: #{event.to_nice_yaml}" }
143
+
144
+ raise "Unknown API version '#{event.object.apiVersion}'" if event.object.apiVersion != "networking.k8s.io/v1"
145
+
146
+ case event.object.kind
147
+ when "IngressClass"
148
+ handle_ingress_class_event(event)
149
+ when "Ingress"
150
+ handle_ingress_event(event)
151
+ else
152
+ raise "Received event for unknown object kind '#{event.object.kind}'"
153
+ end
154
+ end
155
+
156
+ def handle_ingress_class_event(event)
157
+ case event.type
158
+ when "ADDED", "MODIFIED"
159
+ update_ingress_class(event.object)
160
+ when "DELETED"
161
+ remove_ingress_class(event.object.metadata.name)
162
+ else
163
+ logger.error("Unknown event type '#{event.type}'")
164
+ end
165
+ end
166
+
167
+ def handle_ingress_event(event)
168
+ case event.type
169
+ when "ADDED", "MODIFIED"
170
+ update_ingress(event.object)
171
+ when "DELETED"
172
+ # Do nothing
173
+ else
174
+ logger.error("Unknown event type '#{event.type}'")
175
+ end
176
+ end
177
+
178
+ def update_route_table(ingress:)
179
+ metadata = ingress.metadata
180
+
181
+ logger.info "Updating route table for ingress '#{metadata.name}' (namespace: '#{metadata.namespace}')"
182
+ logger.debug { ingress.to_nice_yaml }
183
+
184
+ route_table = @route_table_builder.build(ingress:)
185
+
186
+ logger.debug { route_table.to_nice_yaml }
187
+
188
+ gateway_client.apply_route_table(route_table, field_manager: "gloo-ingress-adapter", force: true)
189
+ end
190
+
191
+ def create_ingress_class_observer
192
+ ResourceObserver.new(
193
+ kind: "ingressclasses",
194
+ client: client_factory.create_client(version: "v1", api: "networking.k8s.io"),
195
+ logger:
196
+ )
197
+ end
198
+
199
+ def create_ingress_observer
200
+ ResourceObserver.new(
201
+ kind: "ingresses",
202
+ client: client_factory.create_client(version: "v1", api: "networking.k8s.io"),
203
+ logger:
204
+ )
205
+ end
206
+
207
+ def ingress_class_names
208
+ @ingress_classes.values.map { |c| c.metadata.name }
209
+ end
210
+
211
+ def log_exception(exception)
212
+ logger.error("#{exception.message} (#{exception.class}/#{exception.class.ancestors})")
213
+ logger.debug(exception.backtrace.join("\n"))
214
+ end
215
+
216
+ def failsafe
217
+ yield
218
+ rescue StandardError => e
219
+ log_exception(e)
220
+ nil
221
+ end
222
+
223
+ def client
224
+ @client ||= client_factory.create_client(version: "v1")
225
+ end
226
+
227
+ def networking_client
228
+ @networking_client ||= client_factory.create_client(version: "v1", api: "networking.k8s.io")
229
+ end
230
+
231
+ def gateway_client
232
+ @gateway_client ||= client_factory.create_client(version: "v1", api: "gateway.solo.io")
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kubeclient"
4
+
5
+ module GlooIngressAdapter
6
+ # Watcher for ingress resources
7
+ class ResourceObserver
8
+ # Thread running an observer
9
+ class ObserverThread < Thread
10
+ attr_reader :observer
11
+
12
+ def initialize(observer:, queue:)
13
+ @observer = observer
14
+
15
+ super { observer.watch(queue:) }
16
+
17
+ self.abort_on_exception = true
18
+ self.name = "observer-#{observer.kind}"
19
+ end
20
+
21
+ def finish
22
+ observer.watcher.finish
23
+ end
24
+ end
25
+
26
+ attr_reader :kind
27
+
28
+ def initialize(kind:, client:, logger:, field_selector: nil)
29
+ @kind = kind
30
+ @client = client
31
+ @logger = logger
32
+ @field_selector = field_selector
33
+ @watcher = nil
34
+ @lock = Mutex.new
35
+ end
36
+
37
+ def start(queue:)
38
+ ObserverThread.new(observer: self, queue:)
39
+ end
40
+
41
+ def watch(queue:)
42
+ logger.info("Watching #{kind}#{field_selector && " with #{field_selector.to_json}"}")
43
+
44
+ retriable do
45
+ self.watcher = client.watch_entities(kind, field_selector:, as: :ros)
46
+
47
+ watcher.each do |event|
48
+ logger.info("Received event: #{event.object.kind} #{event.object.metadata.name} #{event.type.downcase}")
49
+ queue << event
50
+ end
51
+ end
52
+ rescue StandardError => e
53
+ logger.error("Error observing #{kind} resources: #{e.full_message}")
54
+ raise(e)
55
+ ensure
56
+ self.watcher = nil
57
+ end
58
+
59
+ def stop
60
+ watcher&.finish
61
+ end
62
+
63
+ protected
64
+
65
+ attr_reader :client, :logger, :field_selector
66
+
67
+ def watcher
68
+ @lock.synchronize do
69
+ @watcher
70
+ end
71
+ end
72
+
73
+ def watcher=(watcher)
74
+ @lock.synchronize do
75
+ @watcher = watcher
76
+ end
77
+ end
78
+
79
+ def retriable(&)
80
+ retry_proc = proc do |exception|
81
+ logger.error(exception.full_message)
82
+ end
83
+
84
+ Retriable.retriable(tries: 10, base_interval: 1, on_retry: retry_proc, &)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kubeclient/resource"
4
+
5
+ module GlooIngressAdapter
6
+ # Builder responsible to create a route table for an ingress
7
+ class RouteTableBuilder
8
+ def build(ingress:)
9
+ attributes = {
10
+ apiVersion: "gateway.solo.io/v1",
11
+ kind: "RouteTable",
12
+ metadata: {
13
+ name: ingress.metadata.name,
14
+ namespace: ingress.metadata.namespace,
15
+ labels: build_labels(ingress:),
16
+ ownerReferences: [
17
+ {
18
+ apiVersion: "networking.k8s.io/v1",
19
+ kind: "Ingress",
20
+ name: ingress.metadata.name,
21
+ uid: ingress.metadata.uid,
22
+ },
23
+ ],
24
+ },
25
+ spec: {
26
+ routes: build_routes(ingress:),
27
+ },
28
+ }
29
+
30
+ Kubeclient::Resource.new(attributes)
31
+ end
32
+
33
+ protected
34
+
35
+ def build_labels(ingress:)
36
+ {
37
+ "ingress.caperwhite.com/protocol" => ingress.spec.tls ? "https" : "http",
38
+ }
39
+ end
40
+
41
+ def build_routes(ingress:)
42
+ ingress.spec.rules.flat_map do |rule|
43
+ rule.http&.paths&.map do |path|
44
+ build_matcher(ingress:, rule:, path:)
45
+ end
46
+ end.compact
47
+ end
48
+
49
+ def build_matcher(ingress:, rule:, path:)
50
+ service = path.backend.service
51
+
52
+ {
53
+ matchers: [
54
+ {
55
+ headers: headers_matcher(rule:),
56
+ path_type(path.pathType) => path.path,
57
+ }.compact,
58
+ ],
59
+ routeAction: {
60
+ single: {
61
+ kube: {
62
+ ref: {
63
+ name: service.name,
64
+ namespace: service.namespace || ingress.metadata.namespace,
65
+ },
66
+ port: service.port.number || service.port.name,
67
+ },
68
+ },
69
+ },
70
+ }
71
+ end
72
+
73
+ HOST_PATTERN =
74
+
75
+ def headers_matcher(rule:)
76
+ if rule.host
77
+ unless %r{\A(?<star>\*\.)?(?<host>(?:[-a-z0-9]+\.)*[-a-z0-9]+)\z}i =~ rule.host
78
+ raise "Illegal host name '#{rule.host}'"
79
+ end
80
+
81
+ if star
82
+ [{ name: "Host", regex: true, value: "^[^.]+\\.#{host.gsub(".", "\\.")}$" }]
83
+ else
84
+ [{ name: "Host", value: host }]
85
+ end
86
+ end
87
+ end
88
+
89
+ def convert_host(host); end
90
+
91
+ def path_type(type)
92
+ case type.downcase
93
+ when "exact"
94
+ :exact
95
+ when "prefix", "implementationspecific"
96
+ :prefix
97
+ else
98
+ raise "Unknown path type '#{type}'"
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/hash"
5
+
6
+ module GlooIngressAdapter
7
+ module Util
8
+ # Extensions to Resource
9
+ module ResourceExtensions
10
+ def to_json(*args)
11
+ to_h.to_json(*args)
12
+ end
13
+
14
+ def to_nice_yaml
15
+ to_h.deep_stringify_keys.to_yaml
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GlooIngressAdapter
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kubeclient"
4
+ require "zeitwerk"
5
+
6
+ # Main module
7
+ module GlooIngressAdapter
8
+ end
9
+
10
+ loader = Zeitwerk::Loader.for_gem
11
+ loader.setup
12
+
13
+ Kubeclient::Resource.include(GlooIngressAdapter::Util::ResourceExtensions)
metadata ADDED
@@ -0,0 +1,317 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gloo_ingress_adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - CaperWhite GmbH
8
+ autorequire:
9
+ bindir: cmd
10
+ cert_chain: []
11
+ date: 2022-01-21 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: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kubeclient
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: retriable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: zeitwerk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: cheetah
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: debase
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.2'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.2.5.beta2
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '0.2'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 0.2.5.beta2
103
+ - !ruby/object:Gem::Dependency
104
+ name: equatable
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.7'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.7'
117
+ - !ruby/object:Gem::Dependency
118
+ name: hashdiff
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '1.0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: memery
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.4'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '1.4'
145
+ - !ruby/object:Gem::Dependency
146
+ name: pry
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.12'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '0.12'
159
+ - !ruby/object:Gem::Dependency
160
+ name: rake
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '13.0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '13.0'
173
+ - !ruby/object:Gem::Dependency
174
+ name: rspec
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '3.10'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '3.10'
187
+ - !ruby/object:Gem::Dependency
188
+ name: rubocop
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '1.12'
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '1.12'
201
+ - !ruby/object:Gem::Dependency
202
+ name: rubocop-rspec
203
+ requirement: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '2.2'
208
+ type: :development
209
+ prerelease: false
210
+ version_requirements: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '2.2'
215
+ - !ruby/object:Gem::Dependency
216
+ name: ruby-debug-ide
217
+ requirement: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '0.7'
222
+ type: :development
223
+ prerelease: false
224
+ version_requirements: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - "~>"
227
+ - !ruby/object:Gem::Version
228
+ version: '0.7'
229
+ - !ruby/object:Gem::Dependency
230
+ name: simplecov
231
+ requirement: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - "~>"
234
+ - !ruby/object:Gem::Version
235
+ version: '0.21'
236
+ type: :development
237
+ prerelease: false
238
+ version_requirements: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - "~>"
241
+ - !ruby/object:Gem::Version
242
+ version: '0.21'
243
+ - !ruby/object:Gem::Dependency
244
+ name: solargraph
245
+ requirement: !ruby/object:Gem::Requirement
246
+ requirements:
247
+ - - "~>"
248
+ - !ruby/object:Gem::Version
249
+ version: '0.40'
250
+ type: :development
251
+ prerelease: false
252
+ version_requirements: !ruby/object:Gem::Requirement
253
+ requirements:
254
+ - - "~>"
255
+ - !ruby/object:Gem::Version
256
+ version: '0.40'
257
+ - !ruby/object:Gem::Dependency
258
+ name: toml-rb
259
+ requirement: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - "~>"
262
+ - !ruby/object:Gem::Version
263
+ version: '2.1'
264
+ type: :development
265
+ prerelease: false
266
+ version_requirements: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - "~>"
269
+ - !ruby/object:Gem::Version
270
+ version: '2.1'
271
+ description: |2
272
+ This controller allows to integrate routes created from ingresses into a normal (non-ingress) Gloo gateway, thus
273
+ making deploying a separate ingress gateway obsolete. The controller does this by creating route table resources
274
+ from ingress objects in the cluster. These route tables then can be automatically mounted in a Gloo virtual service.
275
+ email:
276
+ - info@caperwhite.com
277
+ executables: []
278
+ extensions: []
279
+ extra_rdoc_files: []
280
+ files:
281
+ - README.md
282
+ - lib/gloo_ingress_adapter.rb
283
+ - lib/gloo_ingress_adapter/client_factory.rb
284
+ - lib/gloo_ingress_adapter/controller.rb
285
+ - lib/gloo_ingress_adapter/resource_observer.rb
286
+ - lib/gloo_ingress_adapter/route_table_builder.rb
287
+ - lib/gloo_ingress_adapter/util/resource_extensions.rb
288
+ - lib/gloo_ingress_adapter/version.rb
289
+ homepage: https://github.com/CaperWhite/gloo-ingress-adapter
290
+ licenses:
291
+ - AGPL-3.0-or-later
292
+ metadata:
293
+ docker_platforms: linux/arm64,linux/amd64
294
+ github_user: CaperWhite
295
+ helm_repo: https://caperwhite.github.io/gloo-ingress-adapter
296
+ homepage_uri: https://github.com/CaperWhite/gloo-ingress-adapter
297
+ rubygems_mfa_required: 'true'
298
+ post_install_message:
299
+ rdoc_options: []
300
+ require_paths:
301
+ - lib
302
+ required_ruby_version: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - ">="
305
+ - !ruby/object:Gem::Version
306
+ version: 3.1.0
307
+ required_rubygems_version: !ruby/object:Gem::Requirement
308
+ requirements:
309
+ - - ">="
310
+ - !ruby/object:Gem::Version
311
+ version: '0'
312
+ requirements: []
313
+ rubygems_version: 3.3.3
314
+ signing_key:
315
+ specification_version: 4
316
+ summary: Use ingress resources with Gloo gateways
317
+ test_files: []