commit cd23fbe4a937503766584eb104379026285bb424
Author: Elias Stepanik <40958815+eliasstepanik@users.noreply.github.com>
Date: Mon May 29 18:42:26 2023 +0200
f
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..cd967fc
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/.idea
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..add57be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+bin/
+obj/
+/packages/
+riderModule.iml
+/_ReSharper.Caches/
\ No newline at end of file
diff --git a/.run/ClearDocker.xml b/.run/ClearDocker.xml
new file mode 100644
index 0000000..71673be
--- /dev/null
+++ b/.run/ClearDocker.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.scripts/clearDocker.sh b/.scripts/clearDocker.sh
new file mode 100644
index 0000000..0a01032
--- /dev/null
+++ b/.scripts/clearDocker.sh
@@ -0,0 +1,24 @@
+# stop all running containers
+echo '####################################################'
+echo 'Stopping running containers (if available)...'
+echo '####################################################'
+docker stop $(docker ps -aq --filter "name=functions") || true
+
+# remove all stopped containers
+echo '####################################################'
+echo 'Removing containers ..'
+echo '####################################################'
+docker rm $(docker ps -aq --filter "name=functions") || true
+
+
+# remove all images
+echo '####################################################'
+echo 'Removing images ...'
+echo '####################################################'
+docker rmi $(docker images -q --filter "reference=*functions*") || true
+
+# remove all stray volumes if any
+#echo '####################################################'
+#echo 'Revoming docker container volumes (if any)'
+#echo '####################################################'
+#docker volume rm $(docker volume ls -q --filter "name=*kassensystembbs2*")
diff --git a/.scripts/runDocker.sh b/.scripts/runDocker.sh
new file mode 100644
index 0000000..77bbba4
--- /dev/null
+++ b/.scripts/runDocker.sh
@@ -0,0 +1,16 @@
+echo '####################################################'
+echo 'Starting Compose Containers ...'
+echo '####################################################'
+docker-compose up -d
+
+
+echo '####################################################'
+echo 'Waiting for Webserver to Start ...'
+echo '####################################################'
+while ! curl --fail --silent --head http://172.29.111.95:8080; do
+ sleep 1
+done
+
+echo '####################################################'
+echo 'Open your Browser at http://172.29.111.95:8080.'
+echo '####################################################'
\ No newline at end of file
diff --git a/Functions/API/FunctionController.cs b/Functions/API/FunctionController.cs
new file mode 100644
index 0000000..69abbb1
--- /dev/null
+++ b/Functions/API/FunctionController.cs
@@ -0,0 +1,60 @@
+using Docker.DotNet.Models;
+using Functions.Services;
+using Functions.Services.Interfaces;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Functions.Controllers;
+
+[ApiController]
+[Route("[controller]")]
+public class FunctionController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly FunctionManager _functionManager;
+
+ public FunctionController(ILogger logger, FunctionManager functionManager)
+ {
+ _logger = logger;
+ _functionManager = functionManager;
+ }
+
+ [HttpPost]
+ public async Task CreateFunction(string functionName, string imageTag)
+ {
+ await _functionManager.CreateFunction(functionName, imageTag);
+ return Ok();
+ }
+
+
+ [HttpPost("{functionName}")]
+ public async Task RunFunctionPost(string functionName,[FromBody] string text)
+ {
+ var response = await _functionManager.RunInstance(functionName,HttpMethod.Post, text);
+ _logger.LogInformation(functionName);
+ return response;
+ }
+ [HttpGet("{functionName}")]
+ public async Task RunFunctionGet(string functionName)
+ {
+ var response = await _functionManager.RunInstance(functionName,HttpMethod.Get);
+ _logger.LogInformation(functionName);
+ return response;
+ }
+
+ [HttpPatch("{functionName}")]
+ public async Task RunFunctionPatch(string functionName,[FromBody] string text)
+ {
+ var response = await _functionManager.RunInstance(functionName,HttpMethod.Patch, text);
+ _logger.LogInformation(functionName);
+ return response;
+ }
+
+
+ [HttpDelete("{functionName}/delete")]
+ public async Task DeleteFunction(string functionName)
+ {
+ await _functionManager.DeleteFunction(functionName);
+ _logger.LogInformation(functionName);
+ return Ok();
+ }
+}
\ No newline at end of file
diff --git a/Functions/API/TestController.cs b/Functions/API/TestController.cs
new file mode 100644
index 0000000..de6d034
--- /dev/null
+++ b/Functions/API/TestController.cs
@@ -0,0 +1,27 @@
+using System.Collections;
+using Docker.DotNet.Models;
+using Functions.Services;
+using Functions.Services.Interfaces;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Functions.Controllers;
+
+[ApiController]
+[Route("[controller]")]
+public class TestController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly IDockerManager _dockerManager;
+
+ public TestController(ILogger logger, IDockerManager dockerManager)
+ {
+ _logger = logger;
+ _dockerManager = dockerManager;
+ }
+
+ [HttpGet(Name = "Containers")]
+ public async Task> Get()
+ {
+ return await _dockerManager.GetContainers();
+ }
+}
\ No newline at end of file
diff --git a/Functions/Data/DB/FunctionsContext.cs b/Functions/Data/DB/FunctionsContext.cs
new file mode 100644
index 0000000..5e3085f
--- /dev/null
+++ b/Functions/Data/DB/FunctionsContext.cs
@@ -0,0 +1,37 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace Functions.Data.DB;
+
+public class FunctionsContext : DbContext
+{
+ public FunctionsContext(DbContextOptions options) : base(options) { }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasMany(p => p.Instances)
+ .WithOne(s => s.Function)
+ .HasForeignKey(s => s.FunctionId);
+ modelBuilder.Entity()
+ .HasMany(p => p.EnvironmentVariables)
+ .WithOne(s => s.Function)
+ .HasForeignKey(s => s.FunctionId);
+ /*modelBuilder.Entity()
+ .HasMany(p => p.SellEntries)
+ .WithOne(s => s.Item);
+
+ modelBuilder.Entity()
+ .HasMany(u => u.SellEntries)
+ .WithOne(e => e.SoldBy);
+
+ modelBuilder.Entity()
+ .HasMany(u => u.Cart)
+ .WithOne(e => e.User);*/
+
+ }
+
+
+ public DbSet Functions { get; set; } = null!;
+ public DbSet Instances { get; set; } = null!;
+ public DbSet EnvironmentVariables { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/Functions/Data/DB/Migrations/20230528164833_Init.Designer.cs b/Functions/Data/DB/Migrations/20230528164833_Init.Designer.cs
new file mode 100644
index 0000000..91b4976
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230528164833_Init.Designer.cs
@@ -0,0 +1,45 @@
+//
+using Functions.Data.DB;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ [DbContext(typeof(FunctionsContext))]
+ [Migration("20230528164833_Init")]
+ partial class Init
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("ImageTag")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Functions");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230528164833_Init.cs b/Functions/Data/DB/Migrations/20230528164833_Init.cs
new file mode 100644
index 0000000..e5d8b36
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230528164833_Init.cs
@@ -0,0 +1,42 @@
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ ///
+ public partial class Init : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterDatabase()
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "Functions",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ Name = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ ImageTag = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Functions", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Functions");
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230528180737_Function.Designer.cs b/Functions/Data/DB/Migrations/20230528180737_Function.Designer.cs
new file mode 100644
index 0000000..0757530
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230528180737_Function.Designer.cs
@@ -0,0 +1,81 @@
+//
+using Functions.Data.DB;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ [DbContext(typeof(FunctionsContext))]
+ [Migration("20230528180737_Function")]
+ partial class Function
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("ImageTag")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Functions");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("FunctionID")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FunctionID");
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.HasOne("Functions.Data.Function", "Function")
+ .WithMany("Instances")
+ .HasForeignKey("FunctionID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Function");
+ });
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Navigation("Instances");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230528180737_Function.cs b/Functions/Data/DB/Migrations/20230528180737_Function.cs
new file mode 100644
index 0000000..2aeb708
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230528180737_Function.cs
@@ -0,0 +1,49 @@
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ ///
+ public partial class Function : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Instances",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ Name = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ FunctionID = table.Column(type: "int", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Instances", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Instances_Functions_FunctionID",
+ column: x => x.FunctionID,
+ principalTable: "Functions",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Instances_FunctionID",
+ table: "Instances",
+ column: "FunctionID");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Instances");
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230528184618_Function2.Designer.cs b/Functions/Data/DB/Migrations/20230528184618_Function2.Designer.cs
new file mode 100644
index 0000000..e480b2f
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230528184618_Function2.Designer.cs
@@ -0,0 +1,85 @@
+//
+using Functions.Data.DB;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ [DbContext(typeof(FunctionsContext))]
+ [Migration("20230528184618_Function2")]
+ partial class Function2
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("ImageTag")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Functions");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("FunctionId")
+ .HasColumnType("int");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FunctionId");
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.HasOne("Functions.Data.Function", "Function")
+ .WithMany("Instances")
+ .HasForeignKey("FunctionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Function");
+ });
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Navigation("Instances");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230528184618_Function2.cs b/Functions/Data/DB/Migrations/20230528184618_Function2.cs
new file mode 100644
index 0000000..c51cf8b
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230528184618_Function2.cs
@@ -0,0 +1,73 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ ///
+ public partial class Function2 : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Instances_Functions_FunctionID",
+ table: "Instances");
+
+ migrationBuilder.RenameColumn(
+ name: "FunctionID",
+ table: "Instances",
+ newName: "FunctionId");
+
+ migrationBuilder.RenameIndex(
+ name: "IX_Instances_FunctionID",
+ table: "Instances",
+ newName: "IX_Instances_FunctionId");
+
+ migrationBuilder.AddColumn(
+ name: "InstanceId",
+ table: "Instances",
+ type: "longtext",
+ nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Instances_Functions_FunctionId",
+ table: "Instances",
+ column: "FunctionId",
+ principalTable: "Functions",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Instances_Functions_FunctionId",
+ table: "Instances");
+
+ migrationBuilder.DropColumn(
+ name: "InstanceId",
+ table: "Instances");
+
+ migrationBuilder.RenameColumn(
+ name: "FunctionId",
+ table: "Instances",
+ newName: "FunctionID");
+
+ migrationBuilder.RenameIndex(
+ name: "IX_Instances_FunctionId",
+ table: "Instances",
+ newName: "IX_Instances_FunctionID");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Instances_Functions_FunctionID",
+ table: "Instances",
+ column: "FunctionID",
+ principalTable: "Functions",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230529102304_Function3.Designer.cs b/Functions/Data/DB/Migrations/20230529102304_Function3.Designer.cs
new file mode 100644
index 0000000..ea70b2b
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230529102304_Function3.Designer.cs
@@ -0,0 +1,122 @@
+//
+using Functions.Data.DB;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ [DbContext(typeof(FunctionsContext))]
+ [Migration("20230529102304_Function3")]
+ partial class Function3
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Functions.Data.Environment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("FunctionId")
+ .HasColumnType("int");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FunctionId");
+
+ b.ToTable("EnvironmentVariables");
+ });
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("ImageTag")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Functions");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("FunctionId")
+ .HasColumnType("int");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FunctionId");
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Functions.Data.Environment", b =>
+ {
+ b.HasOne("Functions.Data.Function", "Function")
+ .WithMany("EnvironmentVariables")
+ .HasForeignKey("FunctionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Function");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.HasOne("Functions.Data.Function", "Function")
+ .WithMany("Instances")
+ .HasForeignKey("FunctionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Function");
+ });
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Navigation("EnvironmentVariables");
+
+ b.Navigation("Instances");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/20230529102304_Function3.cs b/Functions/Data/DB/Migrations/20230529102304_Function3.cs
new file mode 100644
index 0000000..9836fca
--- /dev/null
+++ b/Functions/Data/DB/Migrations/20230529102304_Function3.cs
@@ -0,0 +1,51 @@
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ ///
+ public partial class Function3 : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "EnvironmentVariables",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+ Key = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Value = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ FunctionId = table.Column(type: "int", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_EnvironmentVariables", x => x.Id);
+ table.ForeignKey(
+ name: "FK_EnvironmentVariables_Functions_FunctionId",
+ column: x => x.FunctionId,
+ principalTable: "Functions",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_EnvironmentVariables_FunctionId",
+ table: "EnvironmentVariables",
+ column: "FunctionId");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "EnvironmentVariables");
+ }
+ }
+}
diff --git a/Functions/Data/DB/Migrations/FunctionsContextModelSnapshot.cs b/Functions/Data/DB/Migrations/FunctionsContextModelSnapshot.cs
new file mode 100644
index 0000000..fadc684
--- /dev/null
+++ b/Functions/Data/DB/Migrations/FunctionsContextModelSnapshot.cs
@@ -0,0 +1,119 @@
+//
+using Functions.Data.DB;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Functions.Data.DB.Migrations
+{
+ [DbContext(typeof(FunctionsContext))]
+ partial class FunctionsContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Functions.Data.Environment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("FunctionId")
+ .HasColumnType("int");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FunctionId");
+
+ b.ToTable("EnvironmentVariables");
+ });
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("ImageTag")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Functions");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("FunctionId")
+ .HasColumnType("int");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FunctionId");
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("Functions.Data.Environment", b =>
+ {
+ b.HasOne("Functions.Data.Function", "Function")
+ .WithMany("EnvironmentVariables")
+ .HasForeignKey("FunctionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Function");
+ });
+
+ modelBuilder.Entity("Functions.Data.Instance", b =>
+ {
+ b.HasOne("Functions.Data.Function", "Function")
+ .WithMany("Instances")
+ .HasForeignKey("FunctionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Function");
+ });
+
+ modelBuilder.Entity("Functions.Data.Function", b =>
+ {
+ b.Navigation("EnvironmentVariables");
+
+ b.Navigation("Instances");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Functions/Data/Environment.cs b/Functions/Data/Environment.cs
new file mode 100644
index 0000000..39e273c
--- /dev/null
+++ b/Functions/Data/Environment.cs
@@ -0,0 +1,25 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Functions.Data;
+
+public class Environment
+{
+ public Environment(string key, string value)
+ {
+ Key = key;
+ Value = value;
+ }
+
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [Key]
+ public int Id { get; set; }
+
+ [Required]
+ public string Key { get; set; }
+ [Required]
+ public string Value { get; set; }
+
+ public int FunctionId { get; set; }
+ public Function Function { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/Functions/Data/Function.cs b/Functions/Data/Function.cs
new file mode 100644
index 0000000..f9b7ca7
--- /dev/null
+++ b/Functions/Data/Function.cs
@@ -0,0 +1,27 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Functions.Data;
+
+public class Function
+{
+ public Function(string name, string imageTag)
+ {
+ Name = name;
+ ImageTag = imageTag;
+ }
+
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [Key]
+ public int Id { get; set; }
+
+ [Required]
+ public string Name { get; set; }
+
+ [Required]
+ public string ImageTag { get; set; }
+
+ public List EnvironmentVariables { get; set; }
+
+ public List Instances { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/Functions/Data/Instance.cs b/Functions/Data/Instance.cs
new file mode 100644
index 0000000..62dce12
--- /dev/null
+++ b/Functions/Data/Instance.cs
@@ -0,0 +1,26 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Functions.Data;
+
+public class Instance
+{
+ public Instance(string name, string instanceId)
+ {
+ Name = name;
+ InstanceId = instanceId;
+ }
+
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [Key]
+ public int Id { get; set; }
+
+ [Required]
+ public string InstanceId { get; set; }
+
+ [Required]
+ public string Name { get; set; }
+
+ public int FunctionId { get; set; }
+ public Function Function { get; set; }
+}
\ No newline at end of file
diff --git a/Functions/Dockerfile b/Functions/Dockerfile
new file mode 100644
index 0000000..b36ee4a
--- /dev/null
+++ b/Functions/Dockerfile
@@ -0,0 +1,20 @@
+FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
+WORKDIR /app
+EXPOSE 80
+EXPOSE 443
+
+FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
+WORKDIR /src
+COPY ["Functions/Functions.csproj", "Functions/"]
+RUN dotnet restore "Functions/Functions.csproj"
+COPY . .
+WORKDIR "/src/Functions"
+RUN dotnet build "Functions.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "Functions.csproj" -c Release -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "Functions.dll"]
diff --git a/Functions/Functions.csproj b/Functions/Functions.csproj
new file mode 100644
index 0000000..ff565f2
--- /dev/null
+++ b/Functions/Functions.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net7.0
+ enable
+ enable
+ Linux
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+ .dockerignore
+
+
+ appsettings.json
+
+
+
+
diff --git a/Functions/Program.cs b/Functions/Program.cs
new file mode 100644
index 0000000..0c0e44f
--- /dev/null
+++ b/Functions/Program.cs
@@ -0,0 +1,59 @@
+using System.Text;
+using Functions.Data.DB;
+using Functions.Services;
+using Functions.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+
+//ASPNETCORE_AppConfig__TwilioSecret=my-secret
+builder.Configuration.AddEnvironmentVariables();
+
+builder.Services.AddControllers();
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var connectionString = new StringBuilder();
+connectionString.Append($"server={builder.Configuration["AppConfig:DB:Server"]};");
+connectionString.Append($"port={builder.Configuration["AppConfig:DB:Port"]};");
+connectionString.Append($"user={builder.Configuration["AppConfig:DB:User"]};");
+connectionString.Append($"password={builder.Configuration["AppConfig:DB:Password"]};");
+connectionString.Append($"database={builder.Configuration["AppConfig:DB:Database"]};");
+Console.WriteLine(connectionString.ToString());
+
+var serverVersion = new MySqlServerVersion(new Version(8, 0, 31));
+builder.Services.AddDbContextFactory(
+ dbContextOptions => dbContextOptions
+ .UseMySql(connectionString.ToString(), serverVersion)
+ .LogTo(Console.WriteLine, LogLevel.Debug)
+ .EnableSensitiveDataLogging()
+ .EnableDetailedErrors());
+
+
+builder.Services.AddTransient();
+builder.Services.AddSingleton();
+builder.Services.AddHttpClient()
+ .SetHandlerLifetime(TimeSpan.FromMinutes(5));
+
+
+
+var app = builder.Build();
+
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
+app.MapControllers();
+
+var dbFactory = app.Services.GetRequiredService>();
+var db = await dbFactory.CreateDbContextAsync();
+await db.Database.MigrateAsync();
+
+
+app.Run();
\ No newline at end of file
diff --git a/Functions/Properties/launchSettings.json b/Functions/Properties/launchSettings.json
new file mode 100644
index 0000000..f5826cb
--- /dev/null
+++ b/Functions/Properties/launchSettings.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5051",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7122;http://localhost:5051",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/Functions/Services/DockerManager.cs b/Functions/Services/DockerManager.cs
new file mode 100644
index 0000000..7d1c6dc
--- /dev/null
+++ b/Functions/Services/DockerManager.cs
@@ -0,0 +1,122 @@
+using System.Runtime.InteropServices;
+using Docker.DotNet;
+using Docker.DotNet.Models;
+using Functions.Services.Interfaces;
+using Environment = Functions.Data.Environment;
+
+namespace Functions.Services;
+
+public class DockerManager : IDockerManager
+{
+ private readonly ILogger _logger;
+ private readonly DockerClient _docker;
+ public DockerManager(ILogger logger)
+ {
+ _logger = logger;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ _docker = new DockerClientConfiguration(new Uri("npipe://./pipe/docker_engine")).CreateClient();
+ }
+ else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ _docker = new DockerClientConfiguration(new Uri("unix:///var/run/dockerManager.sock")).CreateClient();
+ }
+ else
+ {
+ throw new Exception("This Platform is not Supported");
+ }
+ }
+
+ public async Task> GetContainers()
+ {
+ //TODO: Add Log Message
+ IList containers = await _docker.Containers.ListContainersAsync(
+ new ContainersListParameters(){
+ Limit = 10,
+ });
+ return containers;
+ }
+
+ public async Task CreateContainer(string image, List envList)
+ {
+ var createContainerParameters = new CreateContainerParameters()
+ {
+ Image = image,
+ Name = "FN-" + new Random().Next(10000, 99999),
+ /*Env = envList,*/ //TODO: Parse the envs
+ NetworkingConfig = new NetworkingConfig()
+ {
+
+ }
+ };
+ var container = await _docker.Containers.CreateContainerAsync(createContainerParameters);
+
+ return new ContainerResponse(createContainerParameters.Name, container.ID);
+ }
+
+ public async void ConnectNetwork(string name, string containerId)
+ {
+ var networkConnectParameters = new NetworkConnectParameters()
+ {
+ Container = containerId
+ };
+ await _docker.Networks.ConnectNetworkAsync(await GetNetworkId(name), networkConnectParameters);
+ }
+
+ public async void StartContainer(string containerId)
+ {
+
+ var containerStartParameters = new ContainerStartParameters();
+ await _docker.Containers.StartContainerAsync(containerId, containerStartParameters);
+ }
+
+ private async Task GetNetworkId(string name)
+ {
+ var response = await _docker.Networks.ListNetworksAsync();
+
+ foreach (var networkResponse in response)
+ {
+ if (networkResponse.Name.Equals(name))
+ {
+ return networkResponse.ID;
+ }
+ }
+
+ return null;
+ }
+
+ public async void DeleteContainer(string containerId)
+ {
+ var parameters = new ContainerRemoveParameters()
+ {
+ Force = true
+ };
+ await _docker.Containers.RemoveContainerAsync(containerId, parameters);
+ }
+
+ public async Task IsRunning(string containerId)
+ {
+ var containers = await GetContainers();
+ foreach (var containerListResponse in containers)
+ {
+ if (containerListResponse.ID.Equals(containerId))
+ {
+ return containerListResponse.State.Equals("Running");
+ }
+ }
+
+ return false;
+ }
+}
+
+public class ContainerResponse
+{
+ public ContainerResponse(string name, string id)
+ {
+ Name = name ?? throw new ArgumentNullException(nameof(name));
+ Id = id ?? throw new ArgumentNullException(nameof(id));
+ }
+
+ public string Name { get; set; }
+ public string Id { get; set; }
+}
\ No newline at end of file
diff --git a/Functions/Services/ExternalEndpointManager.cs b/Functions/Services/ExternalEndpointManager.cs
new file mode 100644
index 0000000..3d67f8a
--- /dev/null
+++ b/Functions/Services/ExternalEndpointManager.cs
@@ -0,0 +1,118 @@
+using System.Text;
+using Functions.Services.Interfaces;
+
+namespace Functions.Services;
+
+public class ExternalEndpointManager : IExternalEndpointManager
+{
+ private readonly ILogger _logger;
+ private readonly HttpClient _httpClient;
+
+ public ExternalEndpointManager(ILogger logger, HttpClient httpClient)
+ {
+ _logger = logger;
+ _httpClient = httpClient;
+ }
+
+ public async Task Get(string hostname)
+ {
+ try
+ {
+ // Send GET request to the API
+ HttpResponseMessage response = await _httpClient.GetAsync($"http://{hostname}");
+
+ // Ensure the response was successful
+ response.EnsureSuccessStatusCode();
+
+ // Read the response content as a string
+ string responseBody = await response.Content.ReadAsStringAsync();
+
+ // Display the response content
+ return responseBody;
+ }
+ catch (HttpRequestException ex)
+ {
+ // Handle any errors that occurred during the request
+ return "error";
+ }
+
+ return "error";
+ }
+
+ public async Task Post(string hostname, string body)
+ {
+ try
+ {
+ var content = new StringContent(body, Encoding.UTF8, "application/json");
+ // Send GET request to the API
+ HttpResponseMessage response = await _httpClient.PostAsync($"http://{hostname}",content);
+
+ // Ensure the response was successful
+ response.EnsureSuccessStatusCode();
+
+ // Read the response content as a string
+ string responseBody = await response.Content.ReadAsStringAsync();
+
+ // Display the response content
+ return responseBody;
+ }
+ catch (HttpRequestException ex)
+ {
+ // Handle any errors that occurred during the request
+ return "error";
+ }
+
+ return "error";
+ }
+
+ public async Task Delete(string hostname)
+ {
+ try
+ {
+ // Send GET request to the API
+ HttpResponseMessage response = await _httpClient.DeleteAsync($"http://{hostname}");
+
+ // Ensure the response was successful
+ response.EnsureSuccessStatusCode();
+
+ // Read the response content as a string
+ string responseBody = await response.Content.ReadAsStringAsync();
+
+ // Display the response content
+ return responseBody;
+ }
+ catch (HttpRequestException ex)
+ {
+ // Handle any errors that occurred during the request
+ return "error";
+ }
+
+ return "error";
+ }
+
+ public async Task Patch(string hostname, string body)
+ {
+ try
+ {
+ var content = new StringContent(body, Encoding.UTF8, "application/json");
+ // Send GET request to the API
+ HttpResponseMessage response = await _httpClient.PatchAsync($"http://{hostname}",content);
+
+ // Ensure the response was successful
+ response.EnsureSuccessStatusCode();
+
+ // Read the response content as a string
+ string responseBody = await response.Content.ReadAsStringAsync();
+
+ // Display the response content
+ return responseBody;
+ }
+ catch (HttpRequestException ex)
+ {
+ // Handle any errors that occurred during the request
+ return "error";
+ }
+
+ return "error";
+ }
+}
\ No newline at end of file
diff --git a/Functions/Services/FunctionManager.cs b/Functions/Services/FunctionManager.cs
new file mode 100644
index 0000000..0e09757
--- /dev/null
+++ b/Functions/Services/FunctionManager.cs
@@ -0,0 +1,99 @@
+using Functions.Data;
+using Functions.Data.DB;
+using Functions.Services.Interfaces;
+using Microsoft.EntityFrameworkCore;
+
+namespace Functions.Services;
+
+public class FunctionManager
+{
+ //TODO: Add Logging
+ private readonly ILogger _logger;
+ private readonly IDockerManager _dockerManager;
+ private readonly IDbContextFactory _dbContextFactory;
+ private readonly IExternalEndpointManager _externalEndpointManager;
+
+ public FunctionManager(ILogger logger, IDockerManager dockerManager, IDbContextFactory dbContextFactory, IExternalEndpointManager externalEndpointManager)
+ {
+ _logger = logger;
+ _dockerManager = dockerManager;
+ _dbContextFactory = dbContextFactory;
+ _externalEndpointManager = externalEndpointManager;
+ }
+
+
+ public async Task CreateFunction(string functionName, string imageTag)
+ {
+ var db = await _dbContextFactory.CreateDbContextAsync();
+ await db.Functions.AddAsync(new Function(functionName, imageTag));
+ await db.SaveChangesAsync();
+ }
+ public async Task DeleteFunction(string functionName)
+ {
+ var db = await _dbContextFactory.CreateDbContextAsync();
+ var function = db.Functions.Include(s => s.Instances).Include(s => s.EnvironmentVariables).First(s => s.Name.Equals(functionName));
+ foreach (var functionInstance in function.Instances)
+ {
+ _dockerManager.DeleteContainer(functionInstance.InstanceId);
+ }
+ db.Functions.Remove(function);
+ await db.SaveChangesAsync();
+ }
+
+ public async Task RunInstance(string functionName, HttpMethod method, string body = "")
+ {
+ var db = await _dbContextFactory.CreateDbContextAsync();
+ var function = db.Functions.Include(s => s.Instances).Include(s => s.EnvironmentVariables).First(s => s.Name.Equals(functionName));
+
+ var container = await _dockerManager.CreateContainer(function.ImageTag,function.EnvironmentVariables);
+ var instance = new Instance(container.Name,container.Id);
+ function.Instances.Add(instance);
+ db.Functions.Update(function);
+ await db.SaveChangesAsync();
+
+ _dockerManager.ConnectNetwork("simplefunctions_functions", instance.InstanceId);
+ _dockerManager.StartContainer(instance.InstanceId);
+
+ //TODO: If not started delete instance
+ //Send Request to Container
+
+ if (method.Equals(HttpMethod.Post))
+ {
+ var message = await _externalEndpointManager.Post(instance.Name, body);
+ if (message.Equals("error"))
+ {
+ _dockerManager.DeleteContainer(instance.InstanceId);
+ return "";
+ }
+
+ return message;
+ }
+ if (method.Equals(HttpMethod.Get))
+ {
+ var message = await _externalEndpointManager.Get(instance.Name);
+ if (message.Equals("error"))
+ {
+ _dockerManager.DeleteContainer(instance.InstanceId);
+ return "";
+ }
+
+ return message;
+ }
+ if (method.Equals(HttpMethod.Patch))
+ {
+ var message = await _externalEndpointManager.Patch(instance.Name, body);
+ if (message.Equals("error"))
+ {
+ _dockerManager.DeleteContainer(instance.InstanceId);
+ return "";
+ }
+
+ return message;
+
+ }
+
+ return "";
+ }
+
+
+}
\ No newline at end of file
diff --git a/Functions/Services/Interfaces/IDockerManager.cs b/Functions/Services/Interfaces/IDockerManager.cs
new file mode 100644
index 0000000..4510229
--- /dev/null
+++ b/Functions/Services/Interfaces/IDockerManager.cs
@@ -0,0 +1,15 @@
+using Docker.DotNet.Models;
+using Functions.Data;
+using Environment = Functions.Data.Environment;
+
+namespace Functions.Services.Interfaces;
+
+public interface IDockerManager
+{
+ public Task> GetContainers();
+ public Task CreateContainer(string image, List envList);
+ public void ConnectNetwork(string name, string containerId);
+ public void StartContainer(string containerId);
+ public void DeleteContainer(string containerId);
+ public Task IsRunning(string containerId);
+}
\ No newline at end of file
diff --git a/Functions/Services/Interfaces/IExternalEndpointManager.cs b/Functions/Services/Interfaces/IExternalEndpointManager.cs
new file mode 100644
index 0000000..32aa04b
--- /dev/null
+++ b/Functions/Services/Interfaces/IExternalEndpointManager.cs
@@ -0,0 +1,11 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Functions.Services.Interfaces;
+
+public interface IExternalEndpointManager
+{
+ public Task Get(string hostname);
+ public Task Post(string hostname, string body);
+ public Task Delete(string hostname);
+ public Task Patch(string hostname, string body);
+}
\ No newline at end of file
diff --git a/Functions/appsettings.Development.json b/Functions/appsettings.Development.json
new file mode 100644
index 0000000..092a492
--- /dev/null
+++ b/Functions/appsettings.Development.json
@@ -0,0 +1,18 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "AppConfig": {
+ "DB": {
+ "Server": "localhost",
+ "Port": "8082",
+ "User": "root",
+ "Password": "testPW",
+ "Database": "Functions"
+ }
+ }
+}
diff --git a/Functions/appsettings.Production.json b/Functions/appsettings.Production.json
new file mode 100644
index 0000000..ff154f2
--- /dev/null
+++ b/Functions/appsettings.Production.json
@@ -0,0 +1,18 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "AppConfig": {
+ "DB": {
+ "Server": "db",
+ "Port": "3306",
+ "User": "root",
+ "Password": "testPW",
+ "Database": "Functions"
+ }
+ }
+}
diff --git a/Functions/appsettings.json b/Functions/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/Functions/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/SimpleFunctions.sln b/SimpleFunctions.sln
new file mode 100644
index 0000000..326155e
--- /dev/null
+++ b/SimpleFunctions.sln
@@ -0,0 +1,27 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Functions", "Functions\Functions.csproj", "{F2CFCA0F-C615-4B7B-89D5-EB6C8EC3ACD1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A7EF8833-F6BE-4EBD-8DB1-5AA8EDB5A16F}"
+ ProjectSection(SolutionItems) = preProject
+ docker-compose.yml = docker-compose.yml
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestFunction", "TestFunction\TestFunction.csproj", "{BC3C484C-EFD0-45E5-BE6E-9212916489E4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F2CFCA0F-C615-4B7B-89D5-EB6C8EC3ACD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CFCA0F-C615-4B7B-89D5-EB6C8EC3ACD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2CFCA0F-C615-4B7B-89D5-EB6C8EC3ACD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2CFCA0F-C615-4B7B-89D5-EB6C8EC3ACD1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BC3C484C-EFD0-45E5-BE6E-9212916489E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC3C484C-EFD0-45E5-BE6E-9212916489E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BC3C484C-EFD0-45E5-BE6E-9212916489E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BC3C484C-EFD0-45E5-BE6E-9212916489E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/TestFunction/Controllers/WeatherForecastController.cs b/TestFunction/Controllers/WeatherForecastController.cs
new file mode 100644
index 0000000..500def6
--- /dev/null
+++ b/TestFunction/Controllers/WeatherForecastController.cs
@@ -0,0 +1,32 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace TestFunction.Controllers;
+
+[ApiController]
+[Route("")]
+public class WeatherForecastController : ControllerBase
+{
+ private static readonly string[] Summaries = new[]
+ {
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ };
+
+ private readonly ILogger _logger;
+
+ public WeatherForecastController(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ [HttpGet(Name = "GetWeatherForecast")]
+ public IEnumerable Get()
+ {
+ return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+ {
+ Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ TemperatureC = Random.Shared.Next(-20, 55),
+ Summary = Summaries[Random.Shared.Next(Summaries.Length)]
+ })
+ .ToArray();
+ }
+}
\ No newline at end of file
diff --git a/TestFunction/Dockerfile b/TestFunction/Dockerfile
new file mode 100644
index 0000000..29a8891
--- /dev/null
+++ b/TestFunction/Dockerfile
@@ -0,0 +1,20 @@
+FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
+WORKDIR /app
+EXPOSE 80
+EXPOSE 443
+
+FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
+WORKDIR /src
+COPY ["TestFunction/TestFunction.csproj", "TestFunction/"]
+RUN dotnet restore "TestFunction/TestFunction.csproj"
+COPY . .
+WORKDIR "/src/TestFunction"
+RUN dotnet build "TestFunction.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "TestFunction.csproj" -c Release -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "TestFunction.dll"]
diff --git a/TestFunction/Program.cs b/TestFunction/Program.cs
new file mode 100644
index 0000000..8264bac
--- /dev/null
+++ b/TestFunction/Program.cs
@@ -0,0 +1,25 @@
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+
+builder.Services.AddControllers();
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
+app.MapControllers();
+
+app.Run();
\ No newline at end of file
diff --git a/TestFunction/Properties/launchSettings.json b/TestFunction/Properties/launchSettings.json
new file mode 100644
index 0000000..b405361
--- /dev/null
+++ b/TestFunction/Properties/launchSettings.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5060",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7090;http://localhost:5060",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/TestFunction/TestFunction.csproj b/TestFunction/TestFunction.csproj
new file mode 100644
index 0000000..9b6c532
--- /dev/null
+++ b/TestFunction/TestFunction.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net7.0
+ enable
+ enable
+ Linux
+ 78de8317-ca29-42e4-8505-95b369d5f64b
+
+
+
+
+
+
+
+
+
+ .dockerignore
+
+
+
+
diff --git a/TestFunction/WeatherForecast.cs b/TestFunction/WeatherForecast.cs
new file mode 100644
index 0000000..12ae3a6
--- /dev/null
+++ b/TestFunction/WeatherForecast.cs
@@ -0,0 +1,12 @@
+namespace TestFunction;
+
+public class WeatherForecast
+{
+ public DateOnly Date { get; set; }
+
+ public int TemperatureC { get; set; }
+
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+
+ public string? Summary { get; set; }
+}
\ No newline at end of file
diff --git a/TestFunction/appsettings.Development.json b/TestFunction/appsettings.Development.json
new file mode 100644
index 0000000..9edf6e9
--- /dev/null
+++ b/TestFunction/appsettings.Development.json
@@ -0,0 +1,17 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AppConfig": {
+ "DB": {
+ "Server": "localhost",
+ "Port": "8082",
+ "User": "root",
+ "Password": "testPW",
+ "Database": "Functions"
+ }
+ }
+}
diff --git a/TestFunction/appsettings.json b/TestFunction/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/TestFunction/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..6356bf1
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,42 @@
+version: "3.3"
+
+volumes:
+ db:
+
+networks:
+ functions:
+
+services:
+ functions:
+ build: ./Functions
+ environment:
+ ASPNETCORE_AppConfig__DB__Server: "db"
+ ASPNETCORE_AppConfig__DB__User: "root"
+ ASPNETCORE_AppConfig__DB__Password: "testPW"
+ ASPNETCORE_AppConfig__DB__Database: "Functions"
+ ASPNETCORE_ENVIRONMENT: "Production"
+ ports:
+ - "8080:80"
+ - "8081:443"
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
+ depends_on:
+ - db
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ networks:
+ - functions
+ db:
+ image: mysql:8.0.31-debian
+ cap_add:
+ - SYS_NICE
+ environment:
+ MYSQL_DATABASE: 'db'
+ MYSQL_ROOT_PASSWORD: 'testPW'
+ volumes:
+ - db:/var/lib/mysql
+ restart: unless-stopped
+ networks:
+ - functions
+ ports:
+ - "8082:3306"
\ No newline at end of file
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..aaac9e0
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "7.0.0",
+ "rollForward": "latestMinor",
+ "allowPrerelease": false
+ }
+}
\ No newline at end of file