mirror of
https://github.com/eliasstepanik/SimpleFunctions.git
synced 2026-01-11 21:58:31 +00:00
f
This commit is contained in:
commit
cd23fbe4a9
25
.dockerignore
Normal file
25
.dockerignore
Normal file
@ -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
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
12
.run/ClearDocker.xml
Normal file
12
.run/ClearDocker.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ClearDocker" type="RunNativeExe" factoryName="Native Executable">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/../../../../Windows/System32/bash.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="$PROJECT_DIR$/.scripts/clearDocker.sh" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<method v="2">
|
||||
<option name="Build Solution" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
24
.scripts/clearDocker.sh
Normal file
24
.scripts/clearDocker.sh
Normal file
@ -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*")
|
||||
16
.scripts/runDocker.sh
Normal file
16
.scripts/runDocker.sh
Normal file
@ -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 '####################################################'
|
||||
60
Functions/API/FunctionController.cs
Normal file
60
Functions/API/FunctionController.cs
Normal file
@ -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<TestController> _logger;
|
||||
private readonly FunctionManager _functionManager;
|
||||
|
||||
public FunctionController(ILogger<TestController> logger, FunctionManager functionManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_functionManager = functionManager;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateFunction(string functionName, string imageTag)
|
||||
{
|
||||
await _functionManager.CreateFunction(functionName, imageTag);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("{functionName}")]
|
||||
public async Task<string> 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<string> RunFunctionGet(string functionName)
|
||||
{
|
||||
var response = await _functionManager.RunInstance(functionName,HttpMethod.Get);
|
||||
_logger.LogInformation(functionName);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPatch("{functionName}")]
|
||||
public async Task<string> 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<IActionResult> DeleteFunction(string functionName)
|
||||
{
|
||||
await _functionManager.DeleteFunction(functionName);
|
||||
_logger.LogInformation(functionName);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
27
Functions/API/TestController.cs
Normal file
27
Functions/API/TestController.cs
Normal file
@ -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<TestController> _logger;
|
||||
private readonly IDockerManager _dockerManager;
|
||||
|
||||
public TestController(ILogger<TestController> logger, IDockerManager dockerManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_dockerManager = dockerManager;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "Containers")]
|
||||
public async Task<IEnumerable<ContainerListResponse>> Get()
|
||||
{
|
||||
return await _dockerManager.GetContainers();
|
||||
}
|
||||
}
|
||||
37
Functions/Data/DB/FunctionsContext.cs
Normal file
37
Functions/Data/DB/FunctionsContext.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Functions.Data.DB;
|
||||
|
||||
public class FunctionsContext : DbContext
|
||||
{
|
||||
public FunctionsContext(DbContextOptions<FunctionsContext> options) : base(options) { }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Function>()
|
||||
.HasMany(p => p.Instances)
|
||||
.WithOne(s => s.Function)
|
||||
.HasForeignKey(s => s.FunctionId);
|
||||
modelBuilder.Entity<Function>()
|
||||
.HasMany(p => p.EnvironmentVariables)
|
||||
.WithOne(s => s.Function)
|
||||
.HasForeignKey(s => s.FunctionId);
|
||||
/*modelBuilder.Entity<Product>()
|
||||
.HasMany(p => p.SellEntries)
|
||||
.WithOne(s => s.Item);
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasMany(u => u.SellEntries)
|
||||
.WithOne(e => e.SoldBy);
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasMany(u => u.Cart)
|
||||
.WithOne(e => e.User);*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
public DbSet<Function> Functions { get; set; } = null!;
|
||||
public DbSet<Instance> Instances { get; set; } = null!;
|
||||
public DbSet<Environment> EnvironmentVariables { get; set; } = null!;
|
||||
}
|
||||
45
Functions/Data/DB/Migrations/20230528164833_Init.Designer.cs
generated
Normal file
45
Functions/Data/DB/Migrations/20230528164833_Init.Designer.cs
generated
Normal file
@ -0,0 +1,45 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageTag")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Functions");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Functions/Data/DB/Migrations/20230528164833_Init.cs
Normal file
42
Functions/Data/DB/Migrations/20230528164833_Init.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Functions.Data.DB.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Init : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Functions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ImageTag = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Functions", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Functions");
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Functions/Data/DB/Migrations/20230528180737_Function.Designer.cs
generated
Normal file
81
Functions/Data/DB/Migrations/20230528180737_Function.Designer.cs
generated
Normal file
@ -0,0 +1,81 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageTag")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Functions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Functions.Data.Instance", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("FunctionID")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
49
Functions/Data/DB/Migrations/20230528180737_Function.cs
Normal file
49
Functions/Data/DB/Migrations/20230528180737_Function.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Functions.Data.DB.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Function : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Instances",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
FunctionID = table.Column<int>(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");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Instances");
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Functions/Data/DB/Migrations/20230528184618_Function2.Designer.cs
generated
Normal file
85
Functions/Data/DB/Migrations/20230528184618_Function2.Designer.cs
generated
Normal file
@ -0,0 +1,85 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageTag")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Functions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Functions.Data.Instance", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("FunctionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("InstanceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Functions/Data/DB/Migrations/20230528184618_Function2.cs
Normal file
73
Functions/Data/DB/Migrations/20230528184618_Function2.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Functions.Data.DB.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Function2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<string>(
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
Functions/Data/DB/Migrations/20230529102304_Function3.Designer.cs
generated
Normal file
122
Functions/Data/DB/Migrations/20230529102304_Function3.Designer.cs
generated
Normal file
@ -0,0 +1,122 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("FunctionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FunctionId");
|
||||
|
||||
b.ToTable("EnvironmentVariables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Functions.Data.Function", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageTag")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Functions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Functions.Data.Instance", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("FunctionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("InstanceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Functions/Data/DB/Migrations/20230529102304_Function3.cs
Normal file
51
Functions/Data/DB/Migrations/20230529102304_Function3.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Functions.Data.DB.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Function3 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EnvironmentVariables",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Key = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Value = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
FunctionId = table.Column<int>(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");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EnvironmentVariables");
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Functions/Data/DB/Migrations/FunctionsContextModelSnapshot.cs
Normal file
119
Functions/Data/DB/Migrations/FunctionsContextModelSnapshot.cs
Normal file
@ -0,0 +1,119 @@
|
||||
// <auto-generated />
|
||||
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<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("FunctionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FunctionId");
|
||||
|
||||
b.ToTable("EnvironmentVariables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Functions.Data.Function", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ImageTag")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Functions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Functions.Data.Instance", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("FunctionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("InstanceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Functions/Data/Environment.cs
Normal file
25
Functions/Data/Environment.cs
Normal file
@ -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!;
|
||||
}
|
||||
27
Functions/Data/Function.cs
Normal file
27
Functions/Data/Function.cs
Normal file
@ -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<Environment> EnvironmentVariables { get; set; }
|
||||
|
||||
public List<Instance> Instances { get; set; } = null!;
|
||||
}
|
||||
26
Functions/Data/Instance.cs
Normal file
26
Functions/Data/Instance.cs
Normal file
@ -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; }
|
||||
}
|
||||
20
Functions/Dockerfile
Normal file
20
Functions/Dockerfile
Normal file
@ -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"]
|
||||
30
Functions/Functions.csproj
Normal file
30
Functions/Functions.csproj
Normal file
@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Docker.DotNet" Version="3.125.15" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
</Content>
|
||||
<Content Update="appsettings.Development.json">
|
||||
<DependentUpon>appsettings.json</DependentUpon>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
59
Functions/Program.cs
Normal file
59
Functions/Program.cs
Normal file
@ -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<FunctionsContext>(
|
||||
dbContextOptions => dbContextOptions
|
||||
.UseMySql(connectionString.ToString(), serverVersion)
|
||||
.LogTo(Console.WriteLine, LogLevel.Debug)
|
||||
.EnableSensitiveDataLogging()
|
||||
.EnableDetailedErrors());
|
||||
|
||||
|
||||
builder.Services.AddTransient<IDockerManager,DockerManager>();
|
||||
builder.Services.AddSingleton<FunctionManager>();
|
||||
builder.Services.AddHttpClient<IExternalEndpointManager, ExternalEndpointManager>()
|
||||
.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<IDbContextFactory<FunctionsContext>>();
|
||||
var db = await dbFactory.CreateDbContextAsync();
|
||||
await db.Database.MigrateAsync();
|
||||
|
||||
|
||||
app.Run();
|
||||
25
Functions/Properties/launchSettings.json
Normal file
25
Functions/Properties/launchSettings.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
122
Functions/Services/DockerManager.cs
Normal file
122
Functions/Services/DockerManager.cs
Normal file
@ -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<DockerManager> _logger;
|
||||
private readonly DockerClient _docker;
|
||||
public DockerManager(ILogger<DockerManager> 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<IList<ContainerListResponse>> GetContainers()
|
||||
{
|
||||
//TODO: Add Log Message
|
||||
IList<ContainerListResponse> containers = await _docker.Containers.ListContainersAsync(
|
||||
new ContainersListParameters(){
|
||||
Limit = 10,
|
||||
});
|
||||
return containers;
|
||||
}
|
||||
|
||||
public async Task<ContainerResponse> CreateContainer(string image, List<Environment> 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<string?> 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<bool> 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; }
|
||||
}
|
||||
118
Functions/Services/ExternalEndpointManager.cs
Normal file
118
Functions/Services/ExternalEndpointManager.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using System.Text;
|
||||
using Functions.Services.Interfaces;
|
||||
|
||||
namespace Functions.Services;
|
||||
|
||||
public class ExternalEndpointManager : IExternalEndpointManager
|
||||
{
|
||||
private readonly ILogger<ExternalEndpointManager> _logger;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public ExternalEndpointManager(ILogger<ExternalEndpointManager> logger, HttpClient httpClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task<string> 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<string> 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<string> 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<string> 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";
|
||||
}
|
||||
}
|
||||
99
Functions/Services/FunctionManager.cs
Normal file
99
Functions/Services/FunctionManager.cs
Normal file
@ -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<FunctionManager> _logger;
|
||||
private readonly IDockerManager _dockerManager;
|
||||
private readonly IDbContextFactory<FunctionsContext> _dbContextFactory;
|
||||
private readonly IExternalEndpointManager _externalEndpointManager;
|
||||
|
||||
public FunctionManager(ILogger<FunctionManager> logger, IDockerManager dockerManager, IDbContextFactory<FunctionsContext> 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<string> 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 "";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
15
Functions/Services/Interfaces/IDockerManager.cs
Normal file
15
Functions/Services/Interfaces/IDockerManager.cs
Normal file
@ -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<IList<ContainerListResponse>> GetContainers();
|
||||
public Task<ContainerResponse> CreateContainer(string image, List<Environment> envList);
|
||||
public void ConnectNetwork(string name, string containerId);
|
||||
public void StartContainer(string containerId);
|
||||
public void DeleteContainer(string containerId);
|
||||
public Task<bool> IsRunning(string containerId);
|
||||
}
|
||||
11
Functions/Services/Interfaces/IExternalEndpointManager.cs
Normal file
11
Functions/Services/Interfaces/IExternalEndpointManager.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Functions.Services.Interfaces;
|
||||
|
||||
public interface IExternalEndpointManager
|
||||
{
|
||||
public Task<string> Get(string hostname);
|
||||
public Task<string> Post(string hostname, string body);
|
||||
public Task<string> Delete(string hostname);
|
||||
public Task<string> Patch(string hostname, string body);
|
||||
}
|
||||
18
Functions/appsettings.Development.json
Normal file
18
Functions/appsettings.Development.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"AppConfig": {
|
||||
"DB": {
|
||||
"Server": "localhost",
|
||||
"Port": "8082",
|
||||
"User": "root",
|
||||
"Password": "testPW",
|
||||
"Database": "Functions"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Functions/appsettings.Production.json
Normal file
18
Functions/appsettings.Production.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"AppConfig": {
|
||||
"DB": {
|
||||
"Server": "db",
|
||||
"Port": "3306",
|
||||
"User": "root",
|
||||
"Password": "testPW",
|
||||
"Database": "Functions"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Functions/appsettings.json
Normal file
9
Functions/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
27
SimpleFunctions.sln
Normal file
27
SimpleFunctions.sln
Normal file
@ -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
|
||||
32
TestFunction/Controllers/WeatherForecastController.cs
Normal file
32
TestFunction/Controllers/WeatherForecastController.cs
Normal file
@ -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<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> 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();
|
||||
}
|
||||
}
|
||||
20
TestFunction/Dockerfile
Normal file
20
TestFunction/Dockerfile
Normal file
@ -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"]
|
||||
25
TestFunction/Program.cs
Normal file
25
TestFunction/Program.cs
Normal file
@ -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();
|
||||
25
TestFunction/Properties/launchSettings.json
Normal file
25
TestFunction/Properties/launchSettings.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
TestFunction/TestFunction.csproj
Normal file
22
TestFunction/TestFunction.csproj
Normal file
@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<UserSecretsId>78de8317-ca29-42e4-8505-95b369d5f64b</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
12
TestFunction/WeatherForecast.cs
Normal file
12
TestFunction/WeatherForecast.cs
Normal file
@ -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; }
|
||||
}
|
||||
17
TestFunction/appsettings.Development.json
Normal file
17
TestFunction/appsettings.Development.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AppConfig": {
|
||||
"DB": {
|
||||
"Server": "localhost",
|
||||
"Port": "8082",
|
||||
"User": "root",
|
||||
"Password": "testPW",
|
||||
"Database": "Functions"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
TestFunction/appsettings.json
Normal file
9
TestFunction/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
42
docker-compose.yml
Normal file
42
docker-compose.yml
Normal file
@ -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"
|
||||
7
global.json
Normal file
7
global.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.0",
|
||||
"rollForward": "latestMinor",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user