Skip to content

Commit

Permalink
Add a workflow for invoking bindings
Browse files Browse the repository at this point in the history
This commit adds a workflow that utilizes two bindings to create
traffic. An Azure Service Bus Queue binding is used to trigger
the generic invoking of another binding. In this case, the test
is setup specifically to call Azure Blob Storage.

Signed-off-by: Hal Spang <[email protected]>
  • Loading branch information
halspang committed Jan 13, 2022
1 parent fe7c0f4 commit 0839010
Show file tree
Hide file tree
Showing 17 changed files with 525 additions and 0 deletions.
91 changes: 91 additions & 0 deletions .github/workflows/binding-workflow-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# ------------------------------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------------------------------

name: build-binding-workflow

on:
push:
branches:
- master
paths:
- 'binding-workflow/**'
- '.github/workflows/**'
pull_request:
branches:
- master
paths:
- 'binding-workflow/**'
- '.github/workflows/**'

jobs:
build:
name: build binding-workflow
runs-on: ubuntu-latest
env:
APP_REGISTRY: dapriotest
APP_IMAGE_NAME: binding-workflow
# TODO: APP_VER needs to be versioned correctly
APP_VER: dev
APP_DIR: ./binding-workflow
ARTIFACT_DIR: ./deploy_artifact
steps:
- name: Check out code
uses: actions/checkout@v2
- name: docker login
if: github.event_name != 'pull_request'
run: |
docker login -u ${{ secrets.DOCKER_REGISTRY_ID }} -p ${{ secrets.DOCKER_REGISTRY_PASS }}
- name: Build binding-workflow docker image
run: |
docker-compose build ${{ env.APP_IMAGE_NAME }}
docker tag ${{ env.APP_IMAGE_NAME }} ${{ env.APP_REGISTRY }}/${{ env.APP_IMAGE_NAME }}:${{ env.APP_VER }}
- name: Push binding-workflow image to dockerhub
if: github.event_name != 'pull_request'
run: |
docker push ${{ env.APP_REGISTRY }}/${{ env.APP_IMAGE_NAME }}:${{ env.APP_VER }}
- name: Copy deployment yaml to archive
run: |
mkdir -p ${{ env.ARTIFACT_DIR }}
cp ./longhaul-test/*.yml ${{ env.ARTIFACT_DIR }}
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: longhaul-test
path: ${{ env.ARTIFACT_DIR }}
deploy:
name: deploy binding-workflow to test cluster
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
env:
APP_NAMESPACE: longhaul-test
TEST_CLUSTER_NAME: dapr-seattle
TEST_RESOURCE_GROUP: dapr-test
ARTIFACT_DIR: ./deploy_artifact
steps:
- name: download artifacts
uses: actions/download-artifact@master
with:
name: longhaul-test
path: ${{ env.ARTIFACT_DIR }}
- name: Login Azure
run: |
az login --service-principal -u ${{ secrets.AZURE_LOGIN_USER }} -p ${{ secrets.AZURE_LOGIN_PASS }} --tenant ${{ secrets.AZURE_TENANT }} --output none
- name: Set up kubeconf for longhaul test environment
run: |
az aks get-credentials -n ${{ env.TEST_CLUSTER_NAME }} -g ${{ env.TEST_RESOURCE_GROUP }}
- name: Set up azure keyvault certificate secret
run: |
az keyvault secret download --vault-name longhaul-kv --name longhaul-cert --encoding base64 --file longhaul-cert.pfx
kubectl delete secret longhaulcert -n longhaul-test --ignore-not-found
kubectl create secret generic longhaulcert -n longhaul-test --from-file=longhaulcert=./longhaul-cert.pfx
- name: Deploy apps to longhaul test environment
run: |
kubectl apply -n ${{ env.APP_NAMESPACE }} -f ${{ env.ARTIFACT_DIR }}/secret-access-role.yml
kubectl apply -n ${{ env.APP_NAMESPACE }} -f ${{ env.ARTIFACT_DIR }}/secret-access-role-binding.yml
kubectl apply -n ${{ env.APP_NAMESPACE }} -f ${{ env.ARTIFACT_DIR }}/azure-keyvault.yml
kubectl apply -n ${{ env.APP_NAMESPACE }} -f ${{ env.ARTIFACT_DIR }}/azure-service-bus-binding.yml
kubectl apply -n ${{ env.APP_NAMESPACE }} -f ${{ env.ARTIFACT_DIR }}/azure-blob-storage-binding.yml
kubectl apply -n ${{ env.APP_NAMESPACE }} -f ${{ env.ARTIFACT_DIR }}/binding-workflow-deploy.yml & kubectl rollout restart -n ${{ env.APP_NAMESPACE }} deploy/binding-workflow-app
6 changes: 6 additions & 0 deletions all.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-co
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pubsub-workflow", "pubsub-workflow\pubsub-workflow.csproj", "{571AD477-1187-4E27-9ADB-CDD6E7370E8C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "binding-workflow", "binding-workflow\binding-workflow.csproj", "{F4B8E286-57E3-4750-849F-A49F62C1AA4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -63,6 +65,10 @@ Global
{571AD477-1187-4E27-9ADB-CDD6E7370E8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{571AD477-1187-4E27-9ADB-CDD6E7370E8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{571AD477-1187-4E27-9ADB-CDD6E7370E8C}.Release|Any CPU.Build.0 = Release|Any CPU
{F4B8E286-57E3-4750-849F-A49F62C1AA4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4B8E286-57E3-4750-849F-A49F62C1AA4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4B8E286-57E3-4750-849F-A49F62C1AA4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4B8E286-57E3-4750-849F-A49F62C1AA4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
37 changes: 37 additions & 0 deletions binding-workflow/Controllers/BindingsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------

using Dapr.Client;
using Dapr.Tests.Common.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BindingWorkflow
{
[ApiController]
public class BindingsController : ControllerBase
{
/// <summary>
/// Handle a message from the Azure Service Bus input binding.
/// </summary>
[HttpPost("/longhaul-invoke-binding")]
public async Task<IActionResult> HandleBinding([FromBody] BindingInvocationMessage message, [FromServices] DaprClient client)
{
Console.WriteLine($"Invoking binding {message.TargetBinding} with operation \"{message.TargetOperation}\" {message.InvocationCount} times.");
for (var i = 0; i < message.InvocationCount; i++)
{
var data = new Dictionary<string, string>(message.Data);
if (message.Metadata.ContainsKey("autoGenId")) {
data.Add("id", Guid.NewGuid().ToString());
}
await client.InvokeBindingAsync(message.TargetBinding, message.TargetOperation, data);
}
Console.WriteLine("Finished invoking binding.");
return Ok();
}
}
}
20 changes: 20 additions & 0 deletions binding-workflow/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ------------------------------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------------------------------

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 3000 9988

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
WORKDIR /app

COPY . ./
RUN dotnet restore binding-workflow/*.csproj
RUN dotnet publish binding-workflow/*.csproj -c Release -o /out

FROM base AS final
WORKDIR /app
COPY --from=build-env /out ./
ENTRYPOINT ["dotnet", "binding-workflow.dll"]
92 changes: 92 additions & 0 deletions binding-workflow/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Dapr.Client;
using Dapr.Tests.Common.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Prometheus;

namespace BindingWorkflow
{
public class Program
{
private static readonly string AzureServiceBusBinding = "longhaul-invoke-binding";
private static readonly string BlobStorageBinding = "longhaul-blob-binding";
private static readonly string CosmosDBBinding = "longhaul-cosmosdb-binding";
private static readonly string CreateOperation = "create";

public static void Main(string[] args)
{
Console.WriteLine("Starting bindings workflow.");

var server = new MetricServer(port: 9988);
server.Start();

var host = CreateHostBuilder(args).Build();

// Invoke the blob storage output binding 200 times every 5 minutes (2400 messages per hour).
var blobPublishTimer = StartInvokingBinding(300, BlobStorageBinding, CreateOperation, 200, new Dictionary<string, string>());
var cosmosDBPublishTimer = StartInvokingBinding(300, CosmosDBBinding, CreateOperation, 200, new Dictionary<string, string>() {
{ "autoGenId", "true"}
});

host.Run();

Console.WriteLine("Exiting bindings workflow.");

blobPublishTimer.Dispose();
cosmosDBPublishTimer.Dispose();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, config) =>
{
config.ClearProviders();
config.AddConsole();

})
.ConfigureWebHostDefaults(webBuilder =>
{
var appSettings = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true)
.AddCommandLine(args)
.Build();

webBuilder.UseStartup<Startup>()
.UseUrls(urls: $"http://*:{appSettings["DaprHTTPAppPort"]}");
});

static internal Timer StartInvokingBinding(int periodInSeconds, string targetBinding, string targetOperation, int invocationCount, Dictionary<string, string> metadata)
{
var client = new DaprClientBuilder().Build();

return new Timer(async (state) =>
{
Console.WriteLine($"Invoking binding to trigger {targetBinding} binding.");
var data = new Dictionary<string, string>();
data.Add("timestamp", DateTime.Now.ToLongTimeString());
data.Add("pk", Guid.NewGuid().ToString());
var request = new BindingInvocationMessage
{
TargetBinding = targetBinding,
TargetOperation = targetOperation,
InvocationCount = invocationCount,
Data = data,
Metadata = metadata,
};
await client.InvokeBindingAsync(AzureServiceBusBinding, CreateOperation, request);
}, null, TimeSpan.FromSeconds(new Random().Next(5, 10)), TimeSpan.FromSeconds(periodInSeconds));
}
}
}
28 changes: 28 additions & 0 deletions binding-workflow/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:19822",
"sslPort": 44364
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"storage_binding": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
46 changes: 46 additions & 0 deletions binding-workflow/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace BindingWorkflow
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDaprClient();
services.AddControllers();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseRouting();

app.UseCloudEvents();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
9 changes: 9 additions & 0 deletions binding-workflow/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
11 changes: 11 additions & 0 deletions binding-workflow/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"DaprHTTPAppPort": "3000"
}
17 changes: 17 additions & 0 deletions binding-workflow/binding-workflow.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapr.AspNetcore" Version="1.5.0" />
<PackageReference Include="Dapr.Client" Version="1.5.0" />
<PackageReference Include="prometheus-net" Version="5.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\common\common.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit 0839010

Please sign in to comment.