flipper 1.2.2 → 1.3.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 +4 -4
- data/.github/workflows/ci.yml +2 -1
- data/.github/workflows/examples.yml +1 -1
- data/README.md +2 -0
- data/docs/images/banner.jpg +0 -0
- data/lib/flipper/adapters/actor_limit.rb +28 -0
- data/lib/flipper/adapters/cache_base.rb +143 -0
- data/lib/flipper/adapters/operation_logger.rb +18 -88
- data/lib/flipper/adapters/read_only.rb +6 -39
- data/lib/flipper/adapters/strict.rb +5 -10
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +36 -17
- data/lib/flipper/cloud/configuration.rb +2 -3
- data/lib/flipper/cloud/telemetry/instrumenter.rb +4 -8
- data/lib/flipper/cloud/telemetry.rb +10 -2
- data/lib/flipper/engine.rb +5 -5
- data/lib/flipper/poller.rb +6 -5
- data/lib/flipper/serializers/gzip.rb +3 -5
- data/lib/flipper/serializers/json.rb +3 -5
- data/lib/flipper/spec/shared_adapter_specs.rb +17 -16
- data/lib/flipper/test/shared_adapter_test.rb +17 -17
- data/lib/flipper/typecast.rb +3 -3
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +3 -1
- data/package-lock.json +41 -0
- data/package.json +10 -0
- data/spec/flipper/adapters/actor_limit_spec.rb +20 -0
- data/spec/flipper/adapters/http_spec.rb +11 -2
- data/spec/flipper/cli_spec.rb +21 -46
- data/spec/flipper/cloud/configuration_spec.rb +2 -1
- data/spec/flipper/cloud/telemetry_spec.rb +52 -0
- data/spec/flipper/cloud_spec.rb +4 -2
- data/spec/flipper/engine_spec.rb +34 -4
- data/spec/flipper/middleware/memoizer_spec.rb +7 -4
- data/spec/support/fail_on_output.rb +8 -0
- data/spec/support/spec_helpers.rb +2 -1
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test_rails/system/test_help_test.rb +1 -1
- metadata +14 -3
@@ -3,9 +3,11 @@ require "delegate"
|
|
3
3
|
module Flipper
|
4
4
|
module Cloud
|
5
5
|
class Telemetry
|
6
|
-
class Instrumenter
|
6
|
+
class Instrumenter
|
7
|
+
attr_reader :instrumenter
|
8
|
+
|
7
9
|
def initialize(cloud_configuration, instrumenter)
|
8
|
-
|
10
|
+
@instrumenter = instrumenter
|
9
11
|
@cloud_configuration = cloud_configuration
|
10
12
|
end
|
11
13
|
|
@@ -14,12 +16,6 @@ module Flipper
|
|
14
16
|
@cloud_configuration.telemetry.record(name, payload)
|
15
17
|
return_value
|
16
18
|
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def instrumenter
|
21
|
-
__getobj__
|
22
|
-
end
|
23
19
|
end
|
24
20
|
end
|
25
21
|
end
|
@@ -160,8 +160,16 @@ module Flipper
|
|
160
160
|
# thus may have a telemetry-interval header for us to respect.
|
161
161
|
response ||= error.response if error && error.respond_to?(:response)
|
162
162
|
|
163
|
-
if response
|
164
|
-
|
163
|
+
if response
|
164
|
+
if Flipper::Typecast.to_boolean(response["telemetry-shutdown"])
|
165
|
+
debug "action=telemetry_shutdown message=The server has requested that telemetry be shut down."
|
166
|
+
stop
|
167
|
+
return
|
168
|
+
end
|
169
|
+
|
170
|
+
if interval = response["telemetry-interval"]
|
171
|
+
self.interval = interval.to_f
|
172
|
+
end
|
165
173
|
end
|
166
174
|
rescue => error
|
167
175
|
error "action=post_to_cloud error=#{error.inspect}"
|
data/lib/flipper/engine.rb
CHANGED
@@ -25,6 +25,7 @@ module Flipper
|
|
25
25
|
log: ENV.fetch('FLIPPER_LOG', 'true').casecmp('true').zero?,
|
26
26
|
cloud_path: "_flipper",
|
27
27
|
strict: default_strict_value,
|
28
|
+
actor_limit: ENV["FLIPPER_ACTOR_LIMIT"]&.to_i || 100,
|
28
29
|
test_help: Flipper::Typecast.to_boolean(ENV["FLIPPER_TEST_HELP"] || Rails.env.test?),
|
29
30
|
)
|
30
31
|
end
|
@@ -65,13 +66,12 @@ module Flipper
|
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
68
|
-
initializer "flipper.
|
69
|
+
initializer "flipper.adapters", after: :load_config_initializers do |app|
|
69
70
|
flipper = app.config.flipper
|
70
71
|
|
71
|
-
|
72
|
-
Flipper.
|
73
|
-
|
74
|
-
end
|
72
|
+
Flipper.configure do |config|
|
73
|
+
config.use Flipper::Adapters::Strict, flipper.strict if flipper.strict
|
74
|
+
config.use Flipper::Adapters::ActorLimit, flipper.actor_limit if flipper.actor_limit
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
data/lib/flipper/poller.rb
CHANGED
@@ -20,6 +20,8 @@ module Flipper
|
|
20
20
|
instances.each {|_, instance| instance.stop }.clear
|
21
21
|
end
|
22
22
|
|
23
|
+
MINIMUM_POLL_INTERVAL = 10
|
24
|
+
|
23
25
|
def initialize(options = {})
|
24
26
|
@thread = nil
|
25
27
|
@pid = Process.pid
|
@@ -30,9 +32,9 @@ module Flipper
|
|
30
32
|
@last_synced_at = Concurrent::AtomicFixnum.new(0)
|
31
33
|
@adapter = Adapters::Memory.new(nil, threadsafe: true)
|
32
34
|
|
33
|
-
if @interval <
|
34
|
-
warn "Flipper::Cloud poll interval must be greater than or equal to
|
35
|
-
@interval =
|
35
|
+
if @interval < MINIMUM_POLL_INTERVAL
|
36
|
+
warn "Flipper::Cloud poll interval must be greater than or equal to #{MINIMUM_POLL_INTERVAL} but was #{@interval}. Setting @interval to #{MINIMUM_POLL_INTERVAL}."
|
37
|
+
@interval = MINIMUM_POLL_INTERVAL
|
36
38
|
end
|
37
39
|
|
38
40
|
@start_automatically = options.fetch(:start_automatically, true)
|
@@ -64,8 +66,7 @@ module Flipper
|
|
64
66
|
# you can instrument these using poller.flipper
|
65
67
|
end
|
66
68
|
|
67
|
-
|
68
|
-
sleep sleep_interval if sleep_interval.positive?
|
69
|
+
sleep interval
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
@@ -3,10 +3,8 @@ require "stringio"
|
|
3
3
|
|
4
4
|
module Flipper
|
5
5
|
module Serializers
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def serialize(source)
|
6
|
+
class Gzip
|
7
|
+
def self.serialize(source)
|
10
8
|
return if source.nil?
|
11
9
|
output = StringIO.new
|
12
10
|
gz = Zlib::GzipWriter.new(output)
|
@@ -15,7 +13,7 @@ module Flipper
|
|
15
13
|
output.string
|
16
14
|
end
|
17
15
|
|
18
|
-
def deserialize(source)
|
16
|
+
def self.deserialize(source)
|
19
17
|
return if source.nil?
|
20
18
|
Zlib::GzipReader.wrap(StringIO.new(source), &:read)
|
21
19
|
end
|
@@ -2,15 +2,13 @@ require "json"
|
|
2
2
|
|
3
3
|
module Flipper
|
4
4
|
module Serializers
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def serialize(source)
|
5
|
+
class Json
|
6
|
+
def self.serialize(source)
|
9
7
|
return if source.nil?
|
10
8
|
JSON.generate(source)
|
11
9
|
end
|
12
10
|
|
13
|
-
def deserialize(source)
|
11
|
+
def self.deserialize(source)
|
14
12
|
return if source.nil?
|
15
13
|
JSON.parse(source)
|
16
14
|
end
|
@@ -108,19 +108,19 @@ RSpec.shared_examples_for 'a flipper adapter' do
|
|
108
108
|
actor22 = Flipper::Actor.new('22')
|
109
109
|
actor_asdf = Flipper::Actor.new('asdf')
|
110
110
|
|
111
|
-
expect(
|
112
|
-
expect(
|
111
|
+
expect(feature.enable(actor22)).to be(true)
|
112
|
+
expect(feature.enable(actor_asdf)).to be(true)
|
113
113
|
|
114
|
-
|
115
|
-
expect(
|
114
|
+
expect(feature).to be_enabled(actor22)
|
115
|
+
expect(feature).to be_enabled(actor_asdf)
|
116
116
|
|
117
|
-
expect(
|
118
|
-
|
119
|
-
expect(
|
117
|
+
expect(feature.disable(actor22)).to be(true)
|
118
|
+
expect(feature).not_to be_enabled(actor22)
|
119
|
+
expect(feature).to be_enabled(actor_asdf)
|
120
120
|
|
121
|
-
expect(
|
122
|
-
|
123
|
-
expect(
|
121
|
+
expect(feature.disable(actor_asdf)).to eq(true)
|
122
|
+
expect(feature).not_to be_enabled(actor22)
|
123
|
+
expect(feature).not_to be_enabled(actor_asdf)
|
124
124
|
end
|
125
125
|
|
126
126
|
it 'can enable, disable and get value for percentage of actors gate' do
|
@@ -182,9 +182,10 @@ RSpec.shared_examples_for 'a flipper adapter' do
|
|
182
182
|
end
|
183
183
|
|
184
184
|
it 'converts the actor value to a string' do
|
185
|
-
|
186
|
-
|
187
|
-
|
185
|
+
actor = Flipper::Actor.new(22)
|
186
|
+
expect(feature).not_to be_enabled(actor)
|
187
|
+
feature.enable_actor actor
|
188
|
+
expect(feature).to be_enabled(actor)
|
188
189
|
end
|
189
190
|
|
190
191
|
it 'converts group value to a string' do
|
@@ -295,9 +296,9 @@ RSpec.shared_examples_for 'a flipper adapter' do
|
|
295
296
|
|
296
297
|
it 'can double enable an actor without error' do
|
297
298
|
actor = Flipper::Actor.new('Flipper::Actor;22')
|
298
|
-
expect(
|
299
|
-
expect(
|
300
|
-
expect(
|
299
|
+
expect(feature.enable(actor)).to be(true)
|
300
|
+
expect(feature.enable(actor)).to be(true)
|
301
|
+
expect(feature).to be_enabled(actor)
|
301
302
|
end
|
302
303
|
|
303
304
|
it 'can double enable a group without error' do
|
@@ -104,19 +104,19 @@ module Flipper
|
|
104
104
|
actor22 = Flipper::Actor.new('22')
|
105
105
|
actor_asdf = Flipper::Actor.new('asdf')
|
106
106
|
|
107
|
-
assert_equal true, @
|
108
|
-
assert_equal true, @
|
107
|
+
assert_equal true, @feature.enable(actor22)
|
108
|
+
assert_equal true, @feature.enable(actor_asdf)
|
109
109
|
|
110
|
-
|
111
|
-
|
110
|
+
assert @feature.enabled?(actor22)
|
111
|
+
assert @feature.enabled?(actor_asdf)
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
assert_equal true, @feature.disable(actor22)
|
114
|
+
refute @feature.enabled?(actor22)
|
115
|
+
assert @feature.enabled?(actor_asdf)
|
116
116
|
|
117
|
-
assert_equal true, @
|
118
|
-
|
119
|
-
|
117
|
+
assert_equal true, @feature.disable(actor_asdf)
|
118
|
+
refute @feature.enabled?(actor22)
|
119
|
+
refute @feature.enabled?(actor_asdf)
|
120
120
|
end
|
121
121
|
|
122
122
|
def test_can_enable_disable_get_value_for_percentage_of_actors_gate
|
@@ -178,10 +178,10 @@ module Flipper
|
|
178
178
|
end
|
179
179
|
|
180
180
|
def test_converts_the_actor_value_to_a_string
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
181
|
+
actor = Flipper::Actor.new(22)
|
182
|
+
refute @feature.enabled?(actor)
|
183
|
+
@feature.enable_actor actor
|
184
|
+
assert @feature.enabled?(actor)
|
185
185
|
end
|
186
186
|
|
187
187
|
def test_converts_group_value_to_a_string
|
@@ -292,9 +292,9 @@ module Flipper
|
|
292
292
|
|
293
293
|
def test_can_double_enable_an_actor_without_error
|
294
294
|
actor = Flipper::Actor.new('Flipper::Actor;22')
|
295
|
-
assert_equal true, @
|
296
|
-
assert_equal true, @
|
297
|
-
|
295
|
+
assert_equal true, @feature.enable(actor)
|
296
|
+
assert_equal true, @feature.enable(actor)
|
297
|
+
assert @feature.enabled?(actor)
|
298
298
|
end
|
299
299
|
|
300
300
|
def test_can_double_enable_a_group_without_error
|
data/lib/flipper/typecast.rb
CHANGED
@@ -3,8 +3,8 @@ require "flipper/serializers/json"
|
|
3
3
|
require "flipper/serializers/gzip"
|
4
4
|
|
5
5
|
module Flipper
|
6
|
-
|
7
|
-
|
6
|
+
class Typecast
|
7
|
+
TRUTH_MAP = {
|
8
8
|
true => true,
|
9
9
|
1 => true,
|
10
10
|
'true' => true,
|
@@ -15,7 +15,7 @@ module Flipper
|
|
15
15
|
#
|
16
16
|
# Returns true or false.
|
17
17
|
def self.to_boolean(value)
|
18
|
-
!!
|
18
|
+
!!TRUTH_MAP[value]
|
19
19
|
end
|
20
20
|
|
21
21
|
# Internal: Convert value to an integer.
|
data/lib/flipper/version.rb
CHANGED
data/lib/flipper.rb
CHANGED
@@ -174,10 +174,12 @@ end
|
|
174
174
|
|
175
175
|
require 'flipper/actor'
|
176
176
|
require 'flipper/adapter'
|
177
|
+
require 'flipper/adapters/wrapper'
|
178
|
+
require 'flipper/adapters/actor_limit'
|
179
|
+
require 'flipper/adapters/instrumented'
|
177
180
|
require 'flipper/adapters/memoizable'
|
178
181
|
require 'flipper/adapters/memory'
|
179
182
|
require 'flipper/adapters/strict'
|
180
|
-
require 'flipper/adapters/instrumented'
|
181
183
|
require 'flipper/adapter_builder'
|
182
184
|
require 'flipper/configuration'
|
183
185
|
require 'flipper/dsl'
|
data/package-lock.json
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"name": "flipper",
|
3
|
+
"lockfileVersion": 3,
|
4
|
+
"requires": true,
|
5
|
+
"packages": {
|
6
|
+
"": {
|
7
|
+
"hasInstallScript": true,
|
8
|
+
"dependencies": {
|
9
|
+
"@popperjs/core": "^2.11.8",
|
10
|
+
"bootstrap": "^5.3.3"
|
11
|
+
}
|
12
|
+
},
|
13
|
+
"node_modules/@popperjs/core": {
|
14
|
+
"version": "2.11.8",
|
15
|
+
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
16
|
+
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
17
|
+
"funding": {
|
18
|
+
"type": "opencollective",
|
19
|
+
"url": "https://opencollective.com/popperjs"
|
20
|
+
}
|
21
|
+
},
|
22
|
+
"node_modules/bootstrap": {
|
23
|
+
"version": "5.3.3",
|
24
|
+
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
25
|
+
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
26
|
+
"funding": [
|
27
|
+
{
|
28
|
+
"type": "github",
|
29
|
+
"url": "https://github.com/sponsors/twbs"
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"type": "opencollective",
|
33
|
+
"url": "https://opencollective.com/bootstrap"
|
34
|
+
}
|
35
|
+
],
|
36
|
+
"peerDependencies": {
|
37
|
+
"@popperjs/core": "^2.11.8"
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
data/package.json
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "flipper/adapters/actor_limit"
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Adapters::ActorLimit do
|
4
|
+
it_should_behave_like 'a flipper adapter' do
|
5
|
+
let(:limit) { 5 }
|
6
|
+
let(:adapter) { Flipper::Adapters::ActorLimit.new(Flipper::Adapters::Memory.new, limit) }
|
7
|
+
|
8
|
+
subject { adapter }
|
9
|
+
|
10
|
+
describe '#enable' do
|
11
|
+
it "fails when limit exceeded" do
|
12
|
+
5.times { |i| feature.enable Flipper::Actor.new("User;#{i}") }
|
13
|
+
|
14
|
+
expect {
|
15
|
+
feature.enable Flipper::Actor.new("User;6")
|
16
|
+
}.to raise_error(Flipper::Adapters::ActorLimit::LimitExceeded)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
require 'flipper/adapters/http'
|
2
2
|
require 'flipper/adapters/pstore'
|
3
|
-
|
3
|
+
|
4
|
+
rack_handler = begin
|
5
|
+
# Rack 3+
|
6
|
+
require 'rackup/handler/webrick'
|
7
|
+
Rackup::Handler::WEBrick
|
8
|
+
rescue LoadError
|
9
|
+
require 'rack/handler/webrick'
|
10
|
+
Rack::Handler::WEBrick
|
11
|
+
end
|
12
|
+
|
4
13
|
|
5
14
|
FLIPPER_SPEC_API_PORT = ENV.fetch('FLIPPER_SPEC_API_PORT', 9001).to_i
|
6
15
|
|
@@ -36,7 +45,7 @@ RSpec.describe Flipper::Adapters::Http do
|
|
36
45
|
],
|
37
46
|
}
|
38
47
|
@server = WEBrick::HTTPServer.new(server_options)
|
39
|
-
@server.mount '/',
|
48
|
+
@server.mount '/', rack_handler, app
|
40
49
|
|
41
50
|
Thread.new { @server.start }
|
42
51
|
Timeout.timeout(1) { :wait until @started }
|
data/spec/flipper/cli_spec.rb
CHANGED
@@ -1,13 +1,33 @@
|
|
1
1
|
require "flipper/cli"
|
2
2
|
|
3
3
|
RSpec.describe Flipper::CLI do
|
4
|
+
let(:stdout) { StringIO.new }
|
5
|
+
let(:stderr) { StringIO.new }
|
6
|
+
let(:cli) { Flipper::CLI.new(stdout: stdout, stderr: stderr) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
# Prentend stdout/stderr a TTY to test colorization
|
10
|
+
allow(stdout).to receive(:tty?).and_return(true)
|
11
|
+
allow(stderr).to receive(:tty?).and_return(true)
|
12
|
+
end
|
13
|
+
|
4
14
|
# Infer the command from the description
|
5
15
|
subject(:argv) do
|
6
16
|
descriptions = self.class.parent_groups.map {|g| g.metadata[:description_args] }.reverse.flatten.drop(1)
|
7
17
|
descriptions.map { |arg| Shellwords.split(arg) }.flatten
|
8
18
|
end
|
9
19
|
|
10
|
-
subject
|
20
|
+
subject do
|
21
|
+
status = 0
|
22
|
+
|
23
|
+
begin
|
24
|
+
cli.run(argv)
|
25
|
+
rescue SystemExit => e
|
26
|
+
status = e.status
|
27
|
+
end
|
28
|
+
|
29
|
+
OpenStruct.new(status: status, stdout: stdout.string, stderr: stderr.string)
|
30
|
+
end
|
11
31
|
|
12
32
|
before do
|
13
33
|
ENV["FLIPPER_REQUIRE"] = "./spec/fixtures/environment"
|
@@ -141,49 +161,4 @@ RSpec.describe Flipper::CLI do
|
|
141
161
|
it { should have_attributes(status: 0, stdout: /enabled.*admins/m) }
|
142
162
|
end
|
143
163
|
end
|
144
|
-
|
145
|
-
context "bundler is not installed" do
|
146
|
-
let(:argv) { "list" }
|
147
|
-
|
148
|
-
around do |example|
|
149
|
-
original_bundler = Bundler
|
150
|
-
begin
|
151
|
-
Object.send(:remove_const, :Bundler)
|
152
|
-
example.run
|
153
|
-
ensure
|
154
|
-
Object.const_set(:Bundler, original_bundler)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
it "should not raise an error" do
|
159
|
-
Flipper.enable(:enabled_feature)
|
160
|
-
Flipper.enable_group(:enabled_groups, :admins)
|
161
|
-
Flipper.add(:disabled_feature)
|
162
|
-
|
163
|
-
expect(subject).to have_attributes(status: 0, stdout: /enabled_feature.*enabled_groups.*disabled_feature/m)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def run(argv)
|
168
|
-
original_stdout = $stdout
|
169
|
-
original_stderr = $stderr
|
170
|
-
|
171
|
-
$stdout = StringIO.new
|
172
|
-
$stderr = StringIO.new
|
173
|
-
status = 0
|
174
|
-
|
175
|
-
# Prentend this a TTY so we can test colorization
|
176
|
-
allow($stdout).to receive(:tty?).and_return(true)
|
177
|
-
|
178
|
-
begin
|
179
|
-
Flipper::CLI.run(argv)
|
180
|
-
rescue SystemExit => e
|
181
|
-
status = e.status
|
182
|
-
end
|
183
|
-
|
184
|
-
OpenStruct.new(status: status, stdout: $stdout.string, stderr: $stderr.string)
|
185
|
-
ensure
|
186
|
-
$stdout = original_stdout
|
187
|
-
$stderr = original_stderr
|
188
|
-
end
|
189
164
|
end
|
@@ -20,7 +20,8 @@ RSpec.describe Flipper::Cloud::Configuration do
|
|
20
20
|
it "can set instrumenter" do
|
21
21
|
instrumenter = Object.new
|
22
22
|
instance = described_class.new(required_options.merge(instrumenter: instrumenter))
|
23
|
-
expect(instance.instrumenter).to
|
23
|
+
expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
|
24
|
+
expect(instance.instrumenter.instrumenter).to be(instrumenter)
|
24
25
|
end
|
25
26
|
|
26
27
|
it "can set read_timeout" do
|
@@ -2,6 +2,12 @@ require 'flipper/cloud/telemetry'
|
|
2
2
|
require 'flipper/cloud/configuration'
|
3
3
|
|
4
4
|
RSpec.describe Flipper::Cloud::Telemetry do
|
5
|
+
before do
|
6
|
+
# Stub polling for features.
|
7
|
+
stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
|
8
|
+
to_return(status: 200, body: "{}")
|
9
|
+
end
|
10
|
+
|
5
11
|
it "phones home and does not update telemetry interval if missing" do
|
6
12
|
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
7
13
|
to_return(status: 200, body: "{}")
|
@@ -42,6 +48,52 @@ RSpec.describe Flipper::Cloud::Telemetry do
|
|
42
48
|
expect(stub).to have_been_requested
|
43
49
|
end
|
44
50
|
|
51
|
+
it "phones home and requests shutdown if telemetry-shutdown header is true" do
|
52
|
+
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
53
|
+
to_return(status: 404, body: "{}", headers: {"telemetry-shutdown" => "true"})
|
54
|
+
|
55
|
+
output = StringIO.new
|
56
|
+
cloud_configuration = Flipper::Cloud::Configuration.new(
|
57
|
+
token: "test",
|
58
|
+
logger: Logger.new(output),
|
59
|
+
logging_enabled: true,
|
60
|
+
)
|
61
|
+
|
62
|
+
# Record some telemetry and stop the threads so we submit a response.
|
63
|
+
telemetry = described_class.new(cloud_configuration)
|
64
|
+
telemetry.record(Flipper::Feature::InstrumentationName, {
|
65
|
+
operation: :enabled?,
|
66
|
+
feature_name: :foo,
|
67
|
+
result: true,
|
68
|
+
})
|
69
|
+
telemetry.stop
|
70
|
+
expect(stub).to have_been_requested
|
71
|
+
expect(output.string).to match(/action=telemetry_shutdown message=The server has requested that telemetry be shut down./)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "phones home and does not shutdown if telemetry shutdown header is missing" do
|
75
|
+
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
76
|
+
to_return(status: 404, body: "{}", headers: {})
|
77
|
+
|
78
|
+
output = StringIO.new
|
79
|
+
cloud_configuration = Flipper::Cloud::Configuration.new(
|
80
|
+
token: "test",
|
81
|
+
logger: Logger.new(output),
|
82
|
+
logging_enabled: true,
|
83
|
+
)
|
84
|
+
|
85
|
+
# Record some telemetry and stop the threads so we submit a response.
|
86
|
+
telemetry = described_class.new(cloud_configuration)
|
87
|
+
telemetry.record(Flipper::Feature::InstrumentationName, {
|
88
|
+
operation: :enabled?,
|
89
|
+
feature_name: :foo,
|
90
|
+
result: true,
|
91
|
+
})
|
92
|
+
telemetry.stop
|
93
|
+
expect(stub).to have_been_requested
|
94
|
+
expect(output.string).not_to match(/action=telemetry_shutdown message=The server has requested that telemetry be shut down./)
|
95
|
+
end
|
96
|
+
|
45
97
|
it "can update telemetry interval from error" do
|
46
98
|
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
47
99
|
to_return(status: 500, body: "{}", headers: {"telemetry-interval" => "120"})
|
data/spec/flipper/cloud_spec.rb
CHANGED
@@ -36,7 +36,8 @@ RSpec.describe Flipper::Cloud do
|
|
36
36
|
expect(client.uri.host).to eq('www.flippercloud.io')
|
37
37
|
expect(client.uri.path).to eq('/adapter')
|
38
38
|
expect(client.headers["flipper-cloud-token"]).to eq(token)
|
39
|
-
expect(@instance.instrumenter).to
|
39
|
+
expect(@instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
|
40
|
+
expect(@instance.instrumenter.instrumenter).to be(Flipper::Instrumenters::Noop)
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
@@ -62,7 +63,8 @@ RSpec.describe Flipper::Cloud do
|
|
62
63
|
it 'can set instrumenter' do
|
63
64
|
instrumenter = Flipper::Instrumenters::Memory.new
|
64
65
|
instance = described_class.new(token: 'asdf', instrumenter: instrumenter)
|
65
|
-
expect(instance.instrumenter).to
|
66
|
+
expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
|
67
|
+
expect(instance.instrumenter.instrumenter).to be(instrumenter)
|
66
68
|
end
|
67
69
|
|
68
70
|
it 'allows wrapping adapter with another adapter like the instrumenter' do
|