diff --git a/src/Opa.Tests/Opa.Tests.csproj b/src/Opa.Tests/Opa.Tests.csproj new file mode 100644 index 00000000..d0e3ee3a --- /dev/null +++ b/src/Opa.Tests/Opa.Tests.csproj @@ -0,0 +1,39 @@ + + + + + Squadron.Opa.Tests + latest + enable + false + + + + Full + true + + + + none + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Opa.Tests/OpaResourceTest.cs b/src/Opa.Tests/OpaResourceTest.cs new file mode 100644 index 00000000..804f3247 --- /dev/null +++ b/src/Opa.Tests/OpaResourceTest.cs @@ -0,0 +1,31 @@ + +using System.Threading.Tasks; +using Squadron; +using Xunit; +using Xunit.Abstractions; + +namespace Opa.Tests +{ + public class OpaResourceTest : IClassFixture + { + private readonly OpaResource _opaResource; + private readonly ITestOutputHelper _helper; + + public OpaResourceTest(OpaResource opaResource, ITestOutputHelper helper) + { + _opaResource = opaResource; + _helper = helper; + } + + [Fact] + public async Task Initializes_Container() + { + // Act + await _opaResource.InitializeAsync(); + _helper.WriteLine($"Client address: {_opaResource.Client.BaseAddress}"); + + // Assert + Assert.True(true); + } + } +} diff --git a/src/Opa.Tests/xunit.runner.json b/src/Opa.Tests/xunit.runner.json new file mode 100644 index 00000000..bd5fcdd2 --- /dev/null +++ b/src/Opa.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "appDomain": "denied", + "parallelizeAssembly": true +} \ No newline at end of file diff --git a/src/Opa/Opa.csproj b/src/Opa/Opa.csproj new file mode 100644 index 00000000..c6c12bbf --- /dev/null +++ b/src/Opa/Opa.csproj @@ -0,0 +1,19 @@ + + + + + Squadron.Opa + latest + latest + enable + + + + + + + + + + + diff --git a/src/Opa/OpaDefaultOptions.cs b/src/Opa/OpaDefaultOptions.cs new file mode 100644 index 00000000..55bbf544 --- /dev/null +++ b/src/Opa/OpaDefaultOptions.cs @@ -0,0 +1,25 @@ +using System; + +namespace Squadron +{ + /// + /// Default OPA resource options + /// + public class OpaDefaultOptions : ContainerResourceOptions, IComposableResourceOption + { + public Type ResourceType => typeof(OpaResource); + + /// + /// Configure resource options + /// + /// + public override void Configure(ContainerResourceBuilder builder) + { + builder + .Name("OPA") + .Image("openpolicyagent/opa:latest") + .InternalPort(8181); + } + } +} + diff --git a/src/Opa/OpaResource.cs b/src/Opa/OpaResource.cs new file mode 100644 index 00000000..7782f94a --- /dev/null +++ b/src/Opa/OpaResource.cs @@ -0,0 +1,75 @@ + +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Xunit; +using System.Text.Json; +using System.IO; +using System.Net.Http.Headers; + +namespace Squadron +{ + + /// + public class OpaResource : OpaResource { } + + /// + /// Represents a mongo database resource that can be used by unit tests. + /// + /// + public class OpaResource + : ContainerResource, + IAsyncLifetime, + IComposableResource + where TOptions : ContainerResourceOptions, new() + { + private HttpClient _client = new(); + public HttpClient Client => _client; + + /// + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + var baseAddress = + $"http://{Manager.Instance.Address}:{Manager.Instance.HostPort}"; + + _client = GetClient(baseAddress); + await Initializer.WaitAsync(new OpaStatus(_client)); + } + + public async Task ListPolicies() + { + Stream contentStream = await ListPolicies(); + T? result = await JsonSerializer.DeserializeAsync(contentStream); + + return result; + } + + public async Task ListPolicies() + { + return await _client.GetStreamAsync("/v1/policies"); + } + + public async Task SetPolicy(string policyContent) + { + HttpContent content = new StringContent(policyContent); + await _client.PutAsync("/v1/policies", content); + } + + public async Task EvalPolicy(string policyId, string input) + { + HttpContent content = new StringContent(input); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + + await _client.PostAsync($"/v1/data/{policyId}", content); + } + + private HttpClient GetClient(string baseAddress) + { + return new() + { + BaseAddress = new Uri(baseAddress), + }; + } + } +} diff --git a/src/Opa/OpaStatus.cs b/src/Opa/OpaStatus.cs new file mode 100644 index 00000000..97671d92 --- /dev/null +++ b/src/Opa/OpaStatus.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Squadron +{ + /// + /// Status checker for Nats + /// + /// + public class OpaStatus : IResourceStatusProvider, IDisposable + { + private readonly HttpClient _httpClient; + + /// + /// Initializes a new instance of the class. + /// + /// Hostname + public OpaStatus(HttpClient httpClient) + { + _httpClient = httpClient; + } + + /// + public async Task IsReadyAsync(CancellationToken cancellationToken) + { + try + { + // HttpResponseMessage? response = await _httpClient.GetAsync( + // new Uri("health", UriKind.Relative), cancellationToken); + + // Debug.Assert(response?.StatusCode == HttpStatusCode.OK); + + return new Status + { + IsReady = true, + Message = "OPA is ready!" + }; + + } + catch (Exception ex) + { + return new Status + { + IsReady = false, + Message = ex.Message + }; + } + } + + /// + public void Dispose() + { + _httpClient.Dispose(); + } + } +} diff --git a/src/Squadron.sln b/src/Squadron.sln index 59b89798..1ef33c41 100644 --- a/src/Squadron.sln +++ b/src/Squadron.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32319.34 MinimumVisualStudioVersion = 15.0.26124.0 @@ -86,6 +86,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nats", "Nats\Nats.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nats.Tests", "Nats.Tests\Nats.Tests.csproj", "{2164CEEC-9F13-44D3-95B9-ED81AAC74189}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Opa", "Opa\Opa.csproj", "{7272EB8A-6DF2-40E3-B747-C3F592F91CAA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Opa.Tests", "Opa.Tests\Opa.Tests.csproj", "{CBF15F2A-AFB3-4908-9EFA-2D71646860CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -588,6 +592,30 @@ Global {2164CEEC-9F13-44D3-95B9-ED81AAC74189}.Release|x64.Build.0 = Release|Any CPU {2164CEEC-9F13-44D3-95B9-ED81AAC74189}.Release|x86.ActiveCfg = Release|Any CPU {2164CEEC-9F13-44D3-95B9-ED81AAC74189}.Release|x86.Build.0 = Release|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Debug|x64.ActiveCfg = Debug|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Debug|x64.Build.0 = Debug|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Debug|x86.ActiveCfg = Debug|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Debug|x86.Build.0 = Debug|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Release|Any CPU.Build.0 = Release|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Release|x64.ActiveCfg = Release|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Release|x64.Build.0 = Release|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Release|x86.ActiveCfg = Release|Any CPU + {7272EB8A-6DF2-40E3-B747-C3F592F91CAA}.Release|x86.Build.0 = Release|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Debug|x64.Build.0 = Debug|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Debug|x86.Build.0 = Debug|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Release|Any CPU.Build.0 = Release|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Release|x64.ActiveCfg = Release|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Release|x64.Build.0 = Release|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Release|x86.ActiveCfg = Release|Any CPU + {CBF15F2A-AFB3-4908-9EFA-2D71646860CC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE