зеркало из https://github.com/microsoft/Oryx.git
Williamhe/add oryx server command (#1208)
* update * update error handling for oryx buildendpoint * update * update * update error handling * update response header with status check urls * update * update * Update for async * Update build server startup * Update build server controller * Add health check * Add build server builder * Remove unnecessary code * Add Microsoft copyright header * Add build server documentation * Remove extra newlines * Update to store JSON flat data store in /store directory * Add buildservice unit tests * Remove unnecessary code * Update build server doc images * Update solution * Update solution Co-authored-by: Arjun Roy Chaudhuri <arroyc@microsoft.com>
This commit is contained in:
Родитель
a62ad97a95
Коммит
b5fbbd4c21
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="Moq" Version="4.10.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\src\BuildServer\BuildServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,141 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using Microsoft.Oryx.BuildServer.Repositories;
|
||||
using Microsoft.Oryx.BuildServer.Services;
|
||||
using Microsoft.Oryx.BuildServer.Services.ArtifactBuilders;
|
||||
using Moq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Tests
|
||||
{
|
||||
public class BuildServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Test_CorrectBuildStatus_OnStartBuild()
|
||||
{
|
||||
// Arrange
|
||||
var build = TestBuild();
|
||||
var mockedBuildRepository = new Mock<IRepository>();
|
||||
var mockedArtifactBuilderFactory = new Mock<IArtifactBuilderFactory>();
|
||||
var mockedArtifactBuilder = new Mock<IArtifactBuilder>();
|
||||
var mockedBuildRunner = new Mock<IBuildRunner>();
|
||||
mockedArtifactBuilder.Setup(x => x.Build(build)).Returns(true);
|
||||
mockedArtifactBuilderFactory.Setup(x => x.CreateArtifactBuilder(build)).Returns(mockedArtifactBuilder.Object);
|
||||
mockedBuildRunner.Setup(x => x.RunInBackground(mockedArtifactBuilder.Object, build, null, null));
|
||||
var testBuildService = TestBuildService(
|
||||
mockedBuildRepository.Object, mockedArtifactBuilderFactory.Object, mockedBuildRunner.Object);
|
||||
|
||||
// Act
|
||||
var buildTest = await testBuildService.StartBuild(build);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(buildTest);
|
||||
Assert.Equal("IN_PROGRESS", buildTest.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Test_GetById_CorrectBuildStatus()
|
||||
{
|
||||
// Arrange
|
||||
var build = TestBuild();
|
||||
var mockedBuildRepository = new Mock<IRepository>();
|
||||
mockedBuildRepository.Setup(x => x.GetById(build.Id)).Returns(build);
|
||||
var mockedArtifactBuilderFactory = new Mock<IArtifactBuilderFactory>();
|
||||
var mockedBuildRunner = new Mock<IBuildRunner>();
|
||||
var testBuildService = TestBuildService(
|
||||
mockedBuildRepository.Object, mockedArtifactBuilderFactory.Object, mockedBuildRunner.Object);
|
||||
|
||||
// Act
|
||||
var buildTest = await testBuildService.GetBuild(build.Id);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(buildTest);
|
||||
Assert.Equal("testId", buildTest.Id);
|
||||
Assert.Equal("testStatus", buildTest.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Test_MarkedCancelled_GetsCorrectBuildStatus()
|
||||
{
|
||||
// Arrange
|
||||
var build = TestBuild();
|
||||
var mockedBuildRepository = new Mock<IRepository>();
|
||||
var mockedArtifactBuilderFactory = new Mock<IArtifactBuilderFactory>();
|
||||
var mockedBuildRunner = new Mock<IBuildRunner>();
|
||||
var testBuildService = TestBuildService(
|
||||
mockedBuildRepository.Object, mockedArtifactBuilderFactory.Object, mockedBuildRunner.Object);
|
||||
|
||||
// Act
|
||||
var buildTest = await testBuildService.MarkCancelled(build);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(buildTest);
|
||||
Assert.Equal("CANCELLED", buildTest.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Test_MarkedCompleted_GetsCorrectBuildStatus()
|
||||
{
|
||||
// Arrange
|
||||
var build = TestBuild();
|
||||
var mockedBuildRepository = new Mock<IRepository>();
|
||||
var mockedArtifactBuilderFactory = new Mock<IArtifactBuilderFactory>();
|
||||
var mockedBuildRunner = new Mock<IBuildRunner>();
|
||||
var testBuildService = TestBuildService(
|
||||
mockedBuildRepository.Object, mockedArtifactBuilderFactory.Object, mockedBuildRunner.Object);
|
||||
|
||||
// Act
|
||||
var buildTest = await testBuildService.MarkCompleted(build);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(buildTest);
|
||||
Assert.Equal("COMPLETED", buildTest.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Test_MarkedFailed_GetsCorrectBuildStatus()
|
||||
{
|
||||
// Arrange
|
||||
var build = TestBuild();
|
||||
var mockedBuildRepository = new Mock<IRepository>();
|
||||
var mockedArtifactBuilderFactory = new Mock<IArtifactBuilderFactory>();
|
||||
var mockedBuildRunner = new Mock<IBuildRunner>();
|
||||
var testBuildService = TestBuildService(
|
||||
mockedBuildRepository.Object, mockedArtifactBuilderFactory.Object, mockedBuildRunner.Object);
|
||||
|
||||
// Act
|
||||
var buildTest = await testBuildService.MarkFailed(build);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(buildTest);
|
||||
Assert.Equal("FAILED", buildTest.Status);
|
||||
}
|
||||
|
||||
private static Build TestBuild()
|
||||
{
|
||||
var build = new Build();
|
||||
build.Id = "testId";
|
||||
build.Status = "testStatus";
|
||||
build.Platform = "testPlatform";
|
||||
build.Version = "1.2.3";
|
||||
build.SourcePath = "/test/sourcePath";
|
||||
build.OutputPath = "/test/outputPath";
|
||||
build.LogPath = "/test/logPath";
|
||||
|
||||
return build;
|
||||
}
|
||||
|
||||
private static BuildService TestBuildService(
|
||||
IRepository testIRepository,
|
||||
IArtifactBuilderFactory testIArtifactBuilderFactory,
|
||||
IBuildRunner testIBuildRunner)
|
||||
{
|
||||
return new BuildService(testIRepository, testIArtifactBuilderFactory, testIBuildRunner);
|
||||
}
|
||||
}
|
||||
}
|
23
Oryx.sln
23
Oryx.sln
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30104.148
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32014.148
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7515BF2B-C485-43A0-AB2F-DC761B6AB151}"
|
||||
EndProject
|
||||
|
@ -96,7 +96,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "src\Oryx.Common\C
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildScriptGenerator.Common.Tests", "tests\BuildScriptGenerator.Common.Tests\BuildScriptGenerator.Common.Tests.csproj", "{3078BCDB-FC35-4826-82A1-9CE094446DE8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoUpdater", "build\tools\AutoUpdater\AutoUpdater.csproj", "{6C4DBA89-3D17-451C-9D89-71A70410D835}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildServer", "src\BuildServer\BuildServer.csproj", "{F13BEF5A-5426-4F77-879F-A7F812F1E336}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildServer.Tests", "BuildServer.Tests\BuildServer.Tests.csproj", "{5BD3E03A-3086-4CF6-950B-215C1E76B7F5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -164,10 +166,14 @@ Global
|
|||
{3078BCDB-FC35-4826-82A1-9CE094446DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3078BCDB-FC35-4826-82A1-9CE094446DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3078BCDB-FC35-4826-82A1-9CE094446DE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6C4DBA89-3D17-451C-9D89-71A70410D835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6C4DBA89-3D17-451C-9D89-71A70410D835}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C4DBA89-3D17-451C-9D89-71A70410D835}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6C4DBA89-3D17-451C-9D89-71A70410D835}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F13BEF5A-5426-4F77-879F-A7F812F1E336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F13BEF5A-5426-4F77-879F-A7F812F1E336}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F13BEF5A-5426-4F77-879F-A7F812F1E336}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F13BEF5A-5426-4F77-879F-A7F812F1E336}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5BD3E03A-3086-4CF6-950B-215C1E76B7F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5BD3E03A-3086-4CF6-950B-215C1E76B7F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5BD3E03A-3086-4CF6-950B-215C1E76B7F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5BD3E03A-3086-4CF6-950B-215C1E76B7F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -194,7 +200,8 @@ Global
|
|||
{ECEEEF09-5053-46D7-B7E8-2711B523D1F6} = {C0150C21-3EC8-4973-B57C-26BC27A4ED57}
|
||||
{93C07A18-89BA-45A2-B818-F2B20E17E0BE} = {C0150C21-3EC8-4973-B57C-26BC27A4ED57}
|
||||
{3078BCDB-FC35-4826-82A1-9CE094446DE8} = {7515BF2B-C485-43A0-AB2F-DC761B6AB151}
|
||||
{6C4DBA89-3D17-451C-9D89-71A70410D835} = {2AA826A1-5647-46B2-9885-20E89050653F}
|
||||
{F13BEF5A-5426-4F77-879F-A7F812F1E336} = {C0150C21-3EC8-4973-B57C-26BC27A4ED57}
|
||||
{5BD3E03A-3086-4CF6-950B-215C1E76B7F5} = {7515BF2B-C485-43A0-AB2F-DC761B6AB151}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3577CC82-8B1B-4B06-829A-3E1CCE78A2C3}
|
||||
|
|
29
README.md
29
README.md
|
@ -110,6 +110,35 @@ docker run --detach --rm \
|
|||
sh -c 'oryx create-script -appPath /app && /run.sh'
|
||||
```
|
||||
|
||||
## Build Server Invocation
|
||||
1. Build the Oryx solution
|
||||
1. ![Build Solutionpng](doc/buildServer/buildSolution.png)
|
||||
1. Create image with oryx and platform binaries
|
||||
1. `time build/buildBuildImages.sh -t ltsversion`
|
||||
1. Run docker to port map, volume mount a directory, specify the image with `oryx build`, and invoke BuildServer
|
||||
1. ```bash
|
||||
docker run -it -p 8086:80 \
|
||||
-v C:\Repo\Oryx\tests\SampleApps\:/tmp/SampleApps \
|
||||
-e "ASPNETCORE_URLS=http://+80" \
|
||||
oryxdevmcr.azurecr.io/public/oryx/build:lts-versions \
|
||||
/opt/buildscriptgen/BuildServer
|
||||
```
|
||||
![Start](doc/buildServer/start.png)
|
||||
1. Invoke build
|
||||
1. ![Post](doc/buildServer/post.png)
|
||||
1. Under the hood `oryx build` is invoked
|
||||
```bash
|
||||
oryx build [sourcePath] \
|
||||
--platform [platform] \
|
||||
--platform-version [version] \
|
||||
--output [outputPath] \
|
||||
--log-file [logPath]
|
||||
```
|
||||
1. Check build status with id `1`
|
||||
1. ![Status](doc/buildServer/status.png)
|
||||
1. Check server healthcheck
|
||||
1. ![Health Check](doc/buildServer/healthCheck.png)
|
||||
|
||||
# Components
|
||||
|
||||
Oryx consists of a build image, a collection of runtime images, a build script generator, and a collection of
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 8.2 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 16 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 38 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 50 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 24 KiB |
|
@ -42,6 +42,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BuildScriptGenerator\BuildScriptGenerator.csproj" />
|
||||
<ProjectReference Include="..\BuildServer\BuildServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\build\FinalPublicKey.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonFlatFileDataStore" Version="2.2.3" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.4" />
|
||||
<PackageReference Include="RunProcessAsTask" Version="1.2.4" />
|
||||
<PackageReference Include="JsonFlatFileDataStore" Version="2.2.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BuildScriptGenerator.Common\BuildScriptGenerator.Common.csproj" />
|
||||
<ProjectReference Include="..\BuildScriptGenerator\BuildScriptGenerator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,63 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using Microsoft.Oryx.BuildServer.Respositories;
|
||||
using Microsoft.Oryx.BuildServer.Services;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Controllers
|
||||
{
|
||||
[Route("api/builds")]
|
||||
[ApiController]
|
||||
public class BuildController : ControllerBase
|
||||
{
|
||||
private readonly IBuildService _buildService;
|
||||
|
||||
public BuildController(IBuildService buildService)
|
||||
{
|
||||
_buildService = buildService;
|
||||
}
|
||||
|
||||
// GET api/<Builds>/5
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType(typeof(Build), StatusCodes.Status201Created)]
|
||||
public async Task<IActionResult> Get(string id)
|
||||
{
|
||||
var build = await _buildService.GetBuild(id);
|
||||
if (build == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(build);
|
||||
}
|
||||
|
||||
// POST api/<Builds>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(Build), StatusCodes.Status201Created)]
|
||||
public async Task<IActionResult> Post([FromBody] Build build)
|
||||
{
|
||||
try
|
||||
{
|
||||
var createdBuild = await _buildService.StartBuild(build);
|
||||
string uri = String.Format("api/builds/{0}", createdBuild.Id);
|
||||
return Created(uri, createdBuild);
|
||||
}
|
||||
catch (ServiceException ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT api/<Builds>/5
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody] Build build)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class HealthController : ControllerBase
|
||||
{
|
||||
|
||||
private readonly ILogger<HealthController> _logger;
|
||||
|
||||
public HealthController(ILogger<HealthController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "health")]
|
||||
public HealthStatus Get()
|
||||
{
|
||||
_logger.LogInformation("Health check ok");
|
||||
return new HealthStatus
|
||||
{
|
||||
Status = "Ok"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Models
|
||||
{
|
||||
public class Build
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string? Status { get; set; }
|
||||
|
||||
public string Platform { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
public string SourcePath { get; set; }
|
||||
public string OutputPath { get; set; }
|
||||
public string LogPath { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Models
|
||||
{
|
||||
public class HealthStatus
|
||||
{
|
||||
public string Status { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:5541",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "build",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"BuildServer": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "build",
|
||||
"environmentVariables": {
|
||||
"DOTNET_USE_POLLING_FILE_WATCHER": "true",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://0.0.0.0:5000"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/build",
|
||||
"publishAllPorts": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using JsonFlatFileDataStore;
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using Microsoft.Oryx.BuildServer.Respositories;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Repositories
|
||||
{
|
||||
public class BuildRepository : IRepository
|
||||
{
|
||||
private readonly DataStore _store;
|
||||
private readonly IDocumentCollection<Build> _collection;
|
||||
public BuildRepository(DataStore store)
|
||||
{
|
||||
_store = store;
|
||||
_collection = _store.GetCollection<Build>();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Build>> GetAll()
|
||||
{
|
||||
return _collection.Find(x => true);
|
||||
}
|
||||
|
||||
public Build? GetById(string id)
|
||||
{
|
||||
var build = _collection.Find(x => x.Id == id).FirstOrDefault();
|
||||
return build;
|
||||
}
|
||||
|
||||
public async Task<Build> Insert(Build build)
|
||||
{
|
||||
if (GetById(build.Id) != null)
|
||||
{
|
||||
throw new IntegrityException(String.Format("Build with id {0} already present", build.Id));
|
||||
}
|
||||
if (await _collection.ReplaceOneAsync(build.Id, build, true))
|
||||
return build;
|
||||
throw new OperationFailedException("Insert Failed");
|
||||
}
|
||||
|
||||
public async Task<Build> Update(Build build)
|
||||
{
|
||||
if(await _collection.UpdateOneAsync(build.Id, build))
|
||||
return build;
|
||||
throw new OperationFailedException("Update Failed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Repositories
|
||||
{
|
||||
public interface IRepository
|
||||
{
|
||||
public Task<Build> Insert(Build build);
|
||||
public Task<IEnumerable<Build>> GetAll();
|
||||
public Task<Build> Update(Build build);
|
||||
public Build? GetById(string id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Respositories
|
||||
{
|
||||
public class ServiceException : Exception
|
||||
{
|
||||
public ServiceException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class IntegrityException : ServiceException
|
||||
{
|
||||
public IntegrityException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class OperationFailedException : ServiceException
|
||||
{
|
||||
public OperationFailedException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services.ArtifactBuilders
|
||||
{
|
||||
public class ArtifactBuilderFactory : IArtifactBuilderFactory
|
||||
{
|
||||
private IArtifactBuilder _builder;
|
||||
|
||||
public ArtifactBuilderFactory(IArtifactBuilder builder)
|
||||
{
|
||||
_builder = builder;
|
||||
}
|
||||
public IArtifactBuilder CreateArtifactBuilder(Build build)
|
||||
{
|
||||
return _builder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services.ArtifactBuilders
|
||||
{
|
||||
class FileOutputHandler
|
||||
{
|
||||
private StreamWriter _fileStream;
|
||||
private readonly ILogger<Builder> _logger;
|
||||
|
||||
public FileOutputHandler(StreamWriter filestream, ILogger<Builder> logger)
|
||||
{
|
||||
_fileStream = filestream;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void handle(object sendingProcess, DataReceivedEventArgs outLine)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(outLine.Data))
|
||||
{
|
||||
_fileStream.Write(outLine.Data + "\n");
|
||||
_logger.LogInformation(outLine.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class Builder : IArtifactBuilder
|
||||
{
|
||||
private readonly ILogger<Builder> _logger;
|
||||
|
||||
public Builder(ILogger<Builder> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool Build(Build build)
|
||||
{
|
||||
var logFilePath = $"{build.LogPath}/{build.Id}.log";
|
||||
var sourcePath = build.SourcePath;
|
||||
var outputPath = $"{build.OutputPath}/{build.Id}";
|
||||
Directory.CreateDirectory(outputPath);
|
||||
var cmd = $"oryx build {sourcePath} --log-file {logFilePath} " +
|
||||
$"--output {outputPath} --platform {build.Platform} " +
|
||||
$"--platform-version {build.Version}";
|
||||
var escapedArgs = cmd.Replace("\"", "\\\"");
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/bash",
|
||||
Arguments = $"-c \"{cmd}\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = true
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
_logger.LogInformation($"Process has started for command: {cmd}");
|
||||
var outputHandler = new FileOutputHandler(new StreamWriter(logFilePath), _logger);
|
||||
process.OutputDataReceived += outputHandler.handle;
|
||||
process.ErrorDataReceived += outputHandler.handle;
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services.ArtifactBuilders
|
||||
{
|
||||
public interface IArtifactBuilder
|
||||
{
|
||||
bool Build(Build build);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services.ArtifactBuilders
|
||||
{
|
||||
public interface IArtifactBuilderFactory
|
||||
{
|
||||
IArtifactBuilder CreateArtifactBuilder(Build build);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using Microsoft.Oryx.BuildServer.Services.ArtifactBuilders;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services
|
||||
{
|
||||
public class BuildRunner : IBuildRunner
|
||||
{
|
||||
public void RunInBackground(IArtifactBuilder builder, Build build, Callback successCallback, Callback failureCallback)
|
||||
{
|
||||
var t = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if(builder.Build(build))
|
||||
{
|
||||
successCallback(build);
|
||||
} else
|
||||
{
|
||||
failureCallback(build);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using Microsoft.Oryx.BuildServer.Repositories;
|
||||
using Microsoft.Oryx.BuildServer.Services.ArtifactBuilders;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services
|
||||
{
|
||||
public class BuildService : IBuildService
|
||||
{
|
||||
private readonly IRepository _buildRepository;
|
||||
private readonly IArtifactBuilderFactory _artifactBuilderFactory;
|
||||
private readonly IBuildRunner _buildRunner;
|
||||
|
||||
public BuildService(IRepository buildRepository, IArtifactBuilderFactory artifactBuilderFactory, IBuildRunner buildRunner)
|
||||
{
|
||||
_buildRepository = buildRepository;
|
||||
_artifactBuilderFactory = artifactBuilderFactory;
|
||||
_buildRunner = buildRunner;
|
||||
}
|
||||
|
||||
public async Task<Build> StartBuild(Build build)
|
||||
{
|
||||
build.Status = "IN_PROGRESS";
|
||||
await _buildRepository.Insert(build);
|
||||
var artifactBuilder = _artifactBuilderFactory.CreateArtifactBuilder(build);
|
||||
_buildRunner.RunInBackground(artifactBuilder, build, MarkCompleted, MarkFailed);
|
||||
return build;
|
||||
}
|
||||
|
||||
public async Task<Build> GetBuild(string id)
|
||||
{
|
||||
var build = _buildRepository.GetById(id);
|
||||
return build;
|
||||
}
|
||||
|
||||
public async Task<Build> MarkCancelled(Build build)
|
||||
{
|
||||
build.Status = "CANCELLED";
|
||||
await _buildRepository.Update(build);
|
||||
return build;
|
||||
}
|
||||
|
||||
public async Task<Build> MarkCompleted(Build build)
|
||||
{
|
||||
build.Status = "COMPLETED";
|
||||
await _buildRepository.Update(build);
|
||||
return build;
|
||||
}
|
||||
|
||||
public async Task<Build> MarkFailed(Build build)
|
||||
{
|
||||
build.Status = "FAILED";
|
||||
await _buildRepository.Update(build);
|
||||
return build;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using Microsoft.Oryx.BuildServer.Services.ArtifactBuilders;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services
|
||||
{
|
||||
public delegate Task<Build> Callback(Build build);
|
||||
|
||||
public interface IBuildRunner
|
||||
{
|
||||
void RunInBackground(IArtifactBuilder builder, Build build, Callback successCallback, Callback FailureCallback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Oryx.BuildServer.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer.Services
|
||||
{
|
||||
public interface IBuildService
|
||||
{
|
||||
Task<Build> StartBuild(Build build);
|
||||
Task<Build> MarkCompleted(Build build);
|
||||
Task<Build> MarkCancelled(Build build);
|
||||
Task<Build> MarkFailed(Build build);
|
||||
Task<Build> GetBuild(string id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// --------------------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using JsonFlatFileDataStore;
|
||||
using Microsoft.Oryx.BuildServer.Repositories;
|
||||
using Microsoft.Oryx.BuildServer.Services;
|
||||
using Microsoft.Oryx.BuildServer.Services.ArtifactBuilders;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Oryx.BuildServer
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
string folderName = "/store";
|
||||
if (!Directory.Exists(folderName))
|
||||
{
|
||||
Directory.CreateDirectory(folderName);
|
||||
}
|
||||
var store = new DataStore("/store/builds.json", keyProperty: "id");
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddMvc();
|
||||
services.AddSingleton<IRepository>(x => new BuildRepository(store));
|
||||
services.AddScoped<IArtifactBuilder, Builder>();
|
||||
services.AddScoped<IArtifactBuilderFactory, ArtifactBuilderFactory>();
|
||||
services.AddScoped<IBuildRunner, BuildRunner>();
|
||||
services.AddScoped<IBuildService, BuildService>();
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.AddSingleton<ILoggerFactory, LoggerFactory>();
|
||||
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
|
||||
services.AddControllers();
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDefaultControllerRoute();
|
||||
// endpoints.MapControllerRoute(
|
||||
// "default",
|
||||
// pattern: "{controller=Build}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://+:80"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
Загрузка…
Ссылка в новой задаче