entitlements-app 1.2.1 → 1.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f374fa7a4154693db9513b354103b917e1a12d58ab9644e75f52bbfb63156e61
4
- data.tar.gz: a942f1ece43a9e3fc3552d9144ce3ce7c095276ecb0706d3e8de975fcfd5288b
3
+ metadata.gz: 18a0176e1783a420da17eee12174e9cc6f17224f980e991149743bd2366a1dbc
4
+ data.tar.gz: 2acf3be9e885ade8181e3fb47adfc87fbfca609bb9978ca84c9d3b6816ae0ad5
5
5
  SHA512:
6
- metadata.gz: 50a19c9c040154f27ea8bdd86dfccf015a2eac1fc6c2f38796c60f6192a0e094e5afd78bcf65e3d5bebaa4e7966662c52e7eb1d135c599d723e23a27d1c08157
7
- data.tar.gz: f4a2256185eaf9b0a1950450e62cee75679aa62e6d208ea50795bc4972bc30348c4877b6befd8f848f55d85702d865f3f3b1c285ea8ecd2e1c39c82384bcbee1
6
+ metadata.gz: 73162232f10dca6c007c7ca5025a385d10ff06b809b1a0daace0fcc3dbcba877337b5a191963d173eeb64ff6635a4dd05f05857fb9ed37201f6cb5dfdd541ef9
7
+ data.tar.gz: ef9e75e2916d78607a4f963f9661c1b36c48055b97d34f0f6286c2f6ba22324f8241d7775a15f37df4bc2a9ed1d823cba5b6553fd9db6e5c9e5050e1cc1fc0a0
@@ -51,6 +51,8 @@ module Entitlements
51
51
  # Done.
52
52
  logger.info "Successfully applied #{Entitlements.cache[:change_count]} change(s)!"
53
53
  0
54
+ ensure
55
+ Entitlements.close_statsd
54
56
  end
55
57
  # :nocov:
56
58
 
data/lib/entitlements.rb CHANGED
@@ -16,9 +16,11 @@ end
16
16
  # :nocov:
17
17
 
18
18
  require "contracts"
19
+ require "datadog/statsd"
19
20
  require "erb"
20
21
  require "logger"
21
22
  require "ostruct"
23
+ require "resolv"
22
24
  require "stringio"
23
25
  require "uri"
24
26
  require "yaml"
@@ -89,6 +91,7 @@ module Entitlements
89
91
  @config_file = nil
90
92
  @config_path_override = nil
91
93
  @person_extra_methods = {}
94
+ @statsd = nil
92
95
 
93
96
  reset_extras!
94
97
  Entitlements::Data::Groups::Calculated.reset!
@@ -354,6 +357,53 @@ module Entitlements
354
357
  end
355
358
  # :nocov:
356
359
 
360
+ def self.statsd
361
+ @statsd ||= build_statsd
362
+ end
363
+
364
+ def self.set_statsd(statsd)
365
+ @statsd = statsd
366
+ end
367
+
368
+ def self.close_statsd
369
+ @statsd&.close
370
+ @statsd = nil
371
+ end
372
+
373
+ def self.build_statsd
374
+ host = Resolv.getaddress(ENV.fetch("DOGSTATSD_HOST", "localhost"))
375
+ port = Integer(ENV.fetch("DOGSTATSD_PORT", 28_125))
376
+ tags = [
377
+ "application:entitlements",
378
+ "kube_pod_name:#{ENV.fetch('KUBE_POD_NAME', 'not-on-kubernetes')}",
379
+ "app_env:#{ENV.fetch('APP_ENV', 'development')}",
380
+ "deployment_id:#{metric_deployment_id}"
381
+ ]
382
+ Datadog::Statsd.new(host, port, tags: tags)
383
+ end
384
+
385
+ def self.metric_deployment_id
386
+ ENV["HEAVEN_DEPLOYMENT_ID"] || ENV["GITHUB_RUN_ID"] || "not-in-deployment"
387
+ end
388
+
389
+ def self.timed_operation(phase:, provider: nil, target: nil, span: "leaf", concurrent: false, count: nil)
390
+ tags = [
391
+ "phase:#{phase}",
392
+ "status:error",
393
+ "span:#{span}",
394
+ "concurrent:#{concurrent}"
395
+ ]
396
+ tags << "provider:#{provider}" if provider
397
+ tags << "target:#{target}" if target
398
+ tags << "count:#{count}" if count
399
+
400
+ statsd.time("entitlements.operation.duration", tags: tags) do
401
+ result = yield
402
+ tags[1] = "status:success"
403
+ result
404
+ end
405
+ end
406
+
357
407
  # Calculate - This runs the entitlements logic to calculate the differences, ultimately
358
408
  # populating a cache and returning a list of actions. The cache and actions can then be
359
409
  # consumed by `execute` to implement the changes.
@@ -363,14 +413,22 @@ module Entitlements
363
413
  # Returns the array of actions.
364
414
  Contract C::None => C::ArrayOf[Entitlements::Models::Action]
365
415
  def self.calculate
416
+ timed_operation(phase: "calculate_total", span: "parent") { calculate_actions }
417
+ end
418
+
419
+ def self.calculate_actions
366
420
  # Load extras that are configured.
367
- Entitlements.load_extras if Entitlements.config.key?("extras")
421
+ if Entitlements.config.key?("extras")
422
+ timed_operation(phase: "load_extras") { Entitlements.load_extras }
423
+ end
368
424
 
369
425
  # Pre-fetch people from configured people data sources.
370
- Entitlements.prefetch_people
426
+ timed_operation(phase: "prefetch_people") { Entitlements.prefetch_people }
371
427
 
372
428
  # Register filters that are configured.
373
- Entitlements.register_filters if Entitlements.config.key?("filters")
429
+ if Entitlements.config.key?("filters")
430
+ timed_operation(phase: "register_filters") { Entitlements.register_filters }
431
+ end
374
432
 
375
433
  # Keep track of the total change count.
376
434
  cache[:change_count] = 0
@@ -385,8 +443,9 @@ module Entitlements
385
443
  Concurrent::Future.execute({ executor: thread_pool }) do
386
444
  group_start = Time.now
387
445
  logger.debug("Begin prefetch and validate for #{group_name}")
388
- obj.prefetch
389
- obj.validate
446
+ provider = Entitlements.config["groups"].fetch(group_name).fetch("type")
447
+ timed_operation(phase: "prefetch", provider: provider, target: group_name, concurrent: true) { obj.prefetch }
448
+ timed_operation(phase: "validate", provider: provider, target: group_name, concurrent: true) { obj.validate }
390
449
  logger.debug("Finished prefetch and validate for #{group_name} in #{Time.now - group_start}")
391
450
  end
392
451
  end
@@ -398,7 +457,8 @@ module Entitlements
398
457
  calc_start = Time.now
399
458
  actions = []
400
459
  Entitlements.child_classes.map do |group_name, obj|
401
- obj.calculate
460
+ provider = Entitlements.config["groups"].fetch(group_name).fetch("type")
461
+ timed_operation(phase: "calculate", provider: provider, target: group_name) { obj.calculate }
402
462
  if obj.change_count > 0
403
463
  logger.debug "Group #{group_name.inspect} contributes #{obj.change_count} change(s)."
404
464
  cache[:change_count] += obj.change_count
@@ -422,8 +482,14 @@ module Entitlements
422
482
  actions: C::ArrayOf[Entitlements::Models::Action]
423
483
  ] => nil
424
484
  def self.execute(actions:)
485
+ timed_operation(phase: "execute_total", span: "parent") { execute_actions(actions: actions) }
486
+ end
487
+
488
+ def self.execute_actions(actions:)
425
489
  # Set up auditors.
426
- Entitlements.auditors.each { |auditor| auditor.setup }
490
+ Entitlements.auditors.each do |auditor|
491
+ timed_operation(phase: "audit_setup", provider: auditor.provider_id) { auditor.setup }
492
+ end
427
493
 
428
494
  # Track any raised exception to pass to the auditors.
429
495
  provider_exception = nil
@@ -433,14 +499,18 @@ module Entitlements
433
499
  # Sort the child classes by priority
434
500
  begin
435
501
  # Pre-apply changes for each class.
436
- Entitlements.child_classes.each do |_, obj|
437
- obj.preapply
502
+ Entitlements.child_classes.each do |group_name, obj|
503
+ provider = Entitlements.config["groups"].fetch(group_name).fetch("type")
504
+ timed_operation(phase: "preapply", provider: provider, target: group_name) { obj.preapply }
438
505
  end
439
506
 
440
507
  # Apply changes from all actions.
441
508
  actions.each do |action|
442
509
  obj = Entitlements.child_classes.fetch(action.ou)
443
- obj.apply(action)
510
+ provider = Entitlements.config["groups"].fetch(action.ou).fetch("type")
511
+ timed_operation(phase: "apply", provider: provider, target: action.ou, count: 1) do
512
+ obj.apply(action)
513
+ end
444
514
  successful_actions.add(action.dn)
445
515
  end
446
516
  rescue => e
@@ -457,11 +527,13 @@ module Entitlements
457
527
  logger.debug "Recording data to #{Entitlements.auditors.size} audit provider(s)"
458
528
  Entitlements.auditors.each do |audit|
459
529
  begin
460
- audit.commit(
461
- actions: actions,
462
- successful_actions: successful_actions,
463
- provider_exception: provider_exception
464
- )
530
+ timed_operation(phase: "audit_commit", provider: audit.provider_id) do
531
+ audit.commit(
532
+ actions: actions,
533
+ successful_actions: successful_actions,
534
+ provider_exception: provider_exception
535
+ )
536
+ end
465
537
  logger.debug "Audit #{audit.description} completed successfully"
466
538
  rescue => e
467
539
  logger.error "Audit #{audit.description} failed: #{e.class} #{e.message}"
@@ -564,7 +636,9 @@ module Entitlements
564
636
 
565
637
  objects = people_data_sources.map do |ds_name, ds_config|
566
638
  people_obj = Entitlements::Data::People.new_from_config(ds_config)
567
- people_obj.read
639
+ timed_operation(phase: "prefetch_people_source", provider: ds_config.fetch("type"), target: ds_name) do
640
+ people_obj.read
641
+ end
568
642
  [ds_name, people_obj]
569
643
  end.to_h
570
644
 
data/lib/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Entitlements
4
4
  module Version
5
- VERSION = "1.2.1"
5
+ VERSION = "1.2.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: entitlements-app
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub, Inc. Security Ops
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-10 00:00:00.000000000 Z
11
+ date: 2026-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.3.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: dogstatsd-ruby
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.7'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.7'
33
47
  - !ruby/object:Gem::Dependency
34
48
  name: faraday
35
49
  requirement: !ruby/object:Gem::Requirement