gloo_ingress_adapter 0.1.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
+ 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: []