• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

me-viper / OpaDotNet.Extensions / 6467548328

10 Oct 2023 09:44AM UTC coverage: 86.706% (+0.4%) from 86.322%
6467548328

push

github

me-viper
chore: Remove redundant entry

85 of 112 branches covered (0.0%)

Branch coverage included in aggregate %.

502 of 565 relevant lines covered (88.85%)

2389.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.07
/src/OpaDotNet.Extensions.AspNetCore/FileSystemPolicySource.cs
1
using Microsoft.Extensions.FileProviders;
2
using Microsoft.Extensions.FileProviders.Physical;
3
using Microsoft.Extensions.Options;
4
using Microsoft.Extensions.Primitives;
5

6
using OpaDotNet.Compilation.Abstractions;
7

8
namespace OpaDotNet.Extensions.AspNetCore;
9

10
public sealed class FileSystemPolicySource : OpaPolicySource
11
{
12
    private readonly IDisposable? _policyWatcher;
13

14
    private readonly PeriodicTimer? _changesMonitor;
15

16
    private readonly CancellationTokenSource _cancellationTokenSource = new();
18✔
17

18
    private bool _needsRecompilation;
19

20
    private bool MonitoringEnabled => Options.Value.MonitoringInterval > TimeSpan.Zero;
38✔
21

22
    private readonly IOptions<RegoCompilerOptions> _compilerOptions;
23

24
    public FileSystemPolicySource(
25
        IRegoCompiler compiler,
26
        IOptions<OpaAuthorizationOptions> options,
27
        IOpaImportsAbiFactory importsAbiFactory,
28
        IOptions<RegoCompilerOptions> compilerOptions,
29
        ILoggerFactory loggerFactory) : base(compiler, options, importsAbiFactory, loggerFactory)
18✔
30
    {
18✔
31
        ArgumentNullException.ThrowIfNull(compilerOptions);
18✔
32

33
        if (string.IsNullOrWhiteSpace(options.Value.PolicyBundlePath))
18!
34
            throw new InvalidOperationException("Compiler requires OpaAuthorizationOptions.PolicyBundlePath specified");
×
35

36
        var path = Options.Value.PolicyBundlePath!;
18✔
37
        _compilerOptions = compilerOptions;
18✔
38

39
        if (!Path.IsPathRooted(Options.Value.PolicyBundlePath!))
18✔
40
            path = Path.GetFullPath(Options.Value.PolicyBundlePath!);
18✔
41

42
        var fileProvider = new PhysicalFileProvider(
18✔
43
            path,
18✔
44
            ExclusionFilters.Sensitive
18✔
45
            );
18✔
46

47
        if (MonitoringEnabled)
18✔
48
        {
2✔
49
            CompositeChangeToken MakePolicyChangeToken() => new(
11✔
50
                new[]
11✔
51
                {
11✔
52
                    fileProvider.Watch("**/*.rego"),
11✔
53
                    fileProvider.Watch("**/data.json"),
11✔
54
                    fileProvider.Watch("**/data.yaml"),
11✔
55
                }
11✔
56
                );
11✔
57

58
            void OnPolicyChange()
59
            {
9✔
60
                Logger.LogDebug("Detected changes in policy");
9✔
61
                _needsRecompilation = true;
9✔
62
            }
9✔
63

64
            _policyWatcher = ChangeToken.OnChange(MakePolicyChangeToken, OnPolicyChange);
2✔
65
            _changesMonitor = new(Options.Value.MonitoringInterval);
2✔
66
        }
2✔
67
    }
18✔
68

69
    protected override async Task<Stream?> CompileBundleFromSource(bool recompiling, CancellationToken cancellationToken = default)
70
    {
124✔
71
        Stream? capsStream = null;
124✔
72

73
        try
74
        {
124✔
75
            if (ImportsAbiFactory.Capabilities != null)
124!
76
                capsStream = ImportsAbiFactory.Capabilities();
×
77

78
            var parameters = new CompilationParameters
124✔
79
            {
124✔
80
                IsBundle = true,
124✔
81
                Entrypoints = Options.Value.Entrypoints,
124✔
82
                CapabilitiesStream = capsStream,
124✔
83
            };
124✔
84

85
            if (!Options.Value.ForceBundleWriter)
124✔
86
            {
123✔
87
                return await Compiler.Compile(
123✔
88
                    Options.Value.PolicyBundlePath!,
123✔
89
                    parameters,
123✔
90
                    cancellationToken: cancellationToken
123✔
91
                    ).ConfigureAwait(false);
123✔
92
            }
93
            else
94
            {
1✔
95
                using var ms = new MemoryStream();
1✔
96

97
                var bundle = BundleWriter.FromDirectory(
1✔
98
                    ms,
1✔
99
                    Options.Value.PolicyBundlePath!,
1✔
100
                    _compilerOptions.Value.Ignore
1✔
101
                    );
1✔
102

103
                await bundle.DisposeAsync().ConfigureAwait(false);
1✔
104
                ms.Seek(0, SeekOrigin.Begin);
1✔
105

106
                return await Compiler.Compile(ms, parameters, cancellationToken).ConfigureAwait(false);
1✔
107
            }
108
        }
109
        finally
110
        {
124✔
111
            if (capsStream != null)
124!
112
                await capsStream.DisposeAsync().ConfigureAwait(false);
×
113
        }
124✔
114
    }
124✔
115

116
    private async Task TrackPolicyChanged(CancellationToken cancellationToken)
117
    {
2✔
118
        if (!MonitoringEnabled || _changesMonitor == null)
2!
119
            return;
×
120

121
        Logger.LogDebug("Watching for policy changes in {Path}", Options.Value.PolicyBundlePath);
2✔
122

123
        while (await _changesMonitor.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false))
13!
124
        {
11✔
125
            try
126
            {
11✔
127
                if (!_needsRecompilation)
11✔
128
                    continue;
5✔
129

130
                if (cancellationToken.IsCancellationRequested)
6!
131
                    break;
×
132

133
                _needsRecompilation = false;
6✔
134

135
                Logger.LogDebug("Detected changes. Recompiling");
6✔
136
                await CompileBundle(true, cancellationToken).ConfigureAwait(false);
6✔
137
                Logger.LogDebug("Recompilation succeeded");
6✔
138
            }
6✔
139
            catch (Exception ex)
×
140
            {
×
141
                Logger.LogError(ex, "Failed to process policy changes");
×
142
                _needsRecompilation = true;
×
143
            }
×
144
        }
6✔
145

146
        Logger.LogDebug("Stopped watching for policy changes");
×
147
    }
×
148

149
    protected override void Dispose(bool disposing)
150
    {
4✔
151
        try
152
        {
4✔
153
            if (disposing)
4✔
154
            {
4✔
155
                _changesMonitor?.Dispose();
4✔
156
                _policyWatcher?.Dispose();
4✔
157
                _cancellationTokenSource.Dispose();
4✔
158
            }
4✔
159
        }
4✔
160
        finally
161
        {
4✔
162
            base.Dispose(disposing);
4✔
163
        }
4✔
164
    }
4✔
165

166
    /// <inheritdoc/>
167
    public override async Task StartAsync(CancellationToken cancellationToken)
168
    {
18✔
169
        await base.StartAsync(cancellationToken).ConfigureAwait(false);
18✔
170

171
        if (MonitoringEnabled)
18✔
172
        {
2✔
173
            _ = Task.Run(() => TrackPolicyChanged(_cancellationTokenSource.Token), cancellationToken);
4✔
174
        }
2✔
175
    }
18✔
176

177
    /// <inheritdoc/>
178
    public override async Task StopAsync(CancellationToken cancellationToken)
179
    {
4✔
180
        await base.StopAsync(cancellationToken).ConfigureAwait(false);
4✔
181

182
        _cancellationTokenSource.Cancel();
4✔
183
        Logger.LogDebug("Stopped");
4✔
184
    }
4✔
185
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc