Browse Source

Allow specifying docker file for tye run (#448)

pull/456/head
Justin Kotalik 6 years ago
committed by GitHub
parent
commit
af82001c30
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      azure-pipelines.yml
  2. 8
      src/Microsoft.Tye.Core/ApplicationFactory.cs
  3. 2
      src/Microsoft.Tye.Core/ConfigModel/ConfigService.cs
  4. 4
      src/Microsoft.Tye.Core/ContainerServiceBuilder.cs
  5. 6
      src/Microsoft.Tye.Core/Serialization/ConfigServiceParser.cs
  6. 2
      src/Microsoft.Tye.Hosting/DockerImagePuller.cs
  7. 29
      src/Microsoft.Tye.Hosting/DockerRunner.cs
  8. 5
      src/Microsoft.Tye.Hosting/Model/DockerRunInfo.cs
  9. 10
      src/tye/ApplicationBuilderExtensions.cs
  10. 77
      test/E2ETest/TyeRunTests.cs
  11. 18
      test/E2ETest/testassets/projects/dockerfile/backend/Dockerfile
  12. 10
      test/E2ETest/testassets/projects/dockerfile/backend/app.js
  13. 374
      test/E2ETest/testassets/projects/dockerfile/backend/package-lock.json
  14. 14
      test/E2ETest/testassets/projects/dockerfile/backend/package.json
  15. 24
      test/E2ETest/testassets/projects/dockerfile/frontend/Program.cs
  16. 27
      test/E2ETest/testassets/projects/dockerfile/frontend/Properties/launchSettings.json
  17. 82
      test/E2ETest/testassets/projects/dockerfile/frontend/Startup.cs
  18. 9
      test/E2ETest/testassets/projects/dockerfile/frontend/appsettings.Development.json
  19. 10
      test/E2ETest/testassets/projects/dockerfile/frontend/appsettings.json
  20. 12
      test/E2ETest/testassets/projects/dockerfile/frontend/frontend.csproj
  21. 11
      test/E2ETest/testassets/projects/dockerfile/tye.yaml
  22. 3
      test/Test.Infrastructure/SkipIfDockerNotRunningAttribute.cs

2
azure-pipelines.yml

@ -75,6 +75,8 @@ stages:
steps:
- checkout: self
clean: true
- script: docker version
- script: docker images
- script: eng\common\cibuild.cmd -configuration $(_BuildConfig) -prepareMachine $(_OfficialBuildArgs)
displayName: Build and Publish
- task: PublishBuildArtifacts@1

8
src/Microsoft.Tye.Core/ApplicationFactory.cs

@ -105,12 +105,14 @@ namespace Microsoft.Tye
// Do k8s by default.
project.ManifestInfo = new KubernetesManifestInfo();
}
else if (!string.IsNullOrEmpty(configService.Image))
else if (!string.IsNullOrEmpty(configService.Image) || !string.IsNullOrEmpty(configService.DockerFile))
{
var container = new ContainerServiceBuilder(configService.Name!, configService.Image)
var container = new ContainerServiceBuilder(configService.Name!, configService.Image!)
{
Args = configService.Args,
Replicas = configService.Replicas ?? 1
Replicas = configService.Replicas ?? 1,
DockerFile = configService.DockerFile != null ? Path.Combine(source.DirectoryName, configService.DockerFile) : null,
DockerFileContext = configService.DockerFileContext != null ? Path.Combine(source.DirectoryName, configService.DockerFileContext) : null
};
service = container;
}

2
src/Microsoft.Tye.Core/ConfigModel/ConfigService.cs

@ -14,6 +14,8 @@ namespace Microsoft.Tye.ConfigModel
public string Name { get; set; } = default!;
public bool External { get; set; }
public string? Image { get; set; }
public string? DockerFile { get; set; }
public string? DockerFileContext { get; set; }
public string? Project { get; set; }
public string? Include { get; set; }
public string? Repository { get; set; }

4
src/Microsoft.Tye.Core/ContainerServiceBuilder.cs

@ -18,6 +18,10 @@ namespace Microsoft.Tye
public string? Args { get; set; }
public string? DockerFile { get; set; }
public string? DockerFileContext { get; set; }
public int Replicas { get; set; } = 1;
public List<EnvironmentVariableBuilder> EnvironmentVariables { get; } = new List<EnvironmentVariableBuilder>();

6
src/Microsoft.Tye.Core/Serialization/ConfigServiceParser.cs

@ -42,6 +42,12 @@ namespace Tye.Serialization
case "image":
service.Image = YamlParser.GetScalarValue(key, child.Value);
break;
case "dockerFile":
service.DockerFile = YamlParser.GetScalarValue(key, child.Value);
break;
case "dockerFileContext":
service.DockerFileContext = YamlParser.GetScalarValue(key, child.Value);
break;
case "project":
service.Project = YamlParser.GetScalarValue(key, child.Value);
break;

2
src/Microsoft.Tye.Hosting/DockerImagePuller.cs

@ -24,7 +24,7 @@ namespace Microsoft.Tye.Hosting
foreach (var s in application.Services)
{
if (s.Value.Description.RunInfo is DockerRunInfo docker)
if (s.Value.Description.RunInfo is DockerRunInfo docker && docker.DockerFile == null)
{
images.Add(docker.Image);
}

29
src/Microsoft.Tye.Hosting/DockerRunner.cs

@ -96,7 +96,7 @@ namespace Microsoft.Tye.Hosting
if (!string.IsNullOrEmpty(application.Network))
{
var dockerNetworkResult = await ProcessUtil.RunAsync("docker", $"network ls --filter \"name={application.Network}\" --format \"{{{{.ID}}}}\"");
var dockerNetworkResult = await ProcessUtil.RunAsync("docker", $"network ls --filter \"name={application.Network}\" --format \"{{{{.ID}}}}\"", throwOnError: false);
if (dockerNetworkResult.ExitCode != 0)
{
_logger.LogError("{Network}: Run docker network ls command failed", application.Network);
@ -131,7 +131,14 @@ namespace Microsoft.Tye.Hosting
_logger.LogInformation("Running docker command {Command}", command);
await ProcessUtil.RunAsync("docker", command);
var dockerNetworkResult = await ProcessUtil.RunAsync("docker", command, throwOnError: false);
if (dockerNetworkResult.ExitCode != 0)
{
_logger.LogInformation("Running docker command with exception info {ExceptionStdOut} {ExceptionStdErr}", dockerNetworkResult.StandardOutput, dockerNetworkResult.StandardError);
throw new CommandException("Run docker network create command failed");
}
}
// Stash information outside of the application services
@ -195,6 +202,22 @@ namespace Microsoft.Tye.Hosting
var volumes = "";
var workingDirectory = docker.WorkingDirectory != null ? $"-w {docker.WorkingDirectory}" : "";
var hostname = "host.docker.internal";
var dockerImage = docker.Image ?? service.Description.Name;
if (docker.DockerFile != null)
{
var dockerBuildResult = await ProcessUtil.RunAsync(
$"docker",
$"build \"{docker.DockerFileContext?.DirectoryName ?? docker.DockerFile.DirectoryName}\" -t {dockerImage} -f \"{docker.DockerFile}\"",
docker.WorkingDirectory,
throwOnError: false);
if (dockerBuildResult.ExitCode != 0)
{
_logger.LogInformation("Running docker command with exception info {ExceptionStdOut} {ExceptionStdErr}", dockerBuildResult.StandardOutput, dockerBuildResult.StandardError);
throw new CommandException("'docker build' failed.");
}
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
@ -297,7 +320,7 @@ namespace Microsoft.Tye.Hosting
}
}
var command = $"run -d {workingDirectory} {volumes} {environmentArguments} {portString} --name {replica} --restart=unless-stopped {docker.Image} {docker.Args ?? ""}";
var command = $"run -d {workingDirectory} {volumes} {environmentArguments} {portString} --name {replica} --restart=unless-stopped {dockerImage} {docker.Args ?? ""}";
_logger.LogInformation("Running image {Image} for {Replica}", docker.Image, replica);

5
src/Microsoft.Tye.Hosting/Model/DockerRunInfo.cs

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.IO;
namespace Microsoft.Tye.Hosting.Model
{
@ -25,5 +26,9 @@ namespace Microsoft.Tye.Hosting.Model
public string? Args { get; }
public string Image { get; }
public FileInfo? DockerFile { get; set; }
public FileInfo? DockerFileContext { get; set; }
}
}

10
src/tye/ApplicationBuilderExtensions.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Tye.Extensions;
using Microsoft.Tye.Hosting.Model;
@ -46,6 +47,15 @@ namespace Microsoft.Tye
{
var dockerRunInfo = new DockerRunInfo(container.Image, container.Args);
if (!string.IsNullOrEmpty(container.DockerFile))
{
dockerRunInfo.DockerFile = new FileInfo(container.DockerFile);
if (!string.IsNullOrEmpty(container.DockerFileContext))
{
dockerRunInfo.DockerFileContext = new FileInfo(container.DockerFileContext);
}
}
foreach (var mapping in container.Volumes)
{
dockerRunInfo.VolumeMappings.Add(new DockerVolume(mapping.Source, mapping.Name, mapping.Target));

77
test/E2ETest/TyeRunTests.cs

@ -622,6 +622,83 @@ services:
});
}
[ConditionalFact]
[SkipIfDockerNotRunning]
public async Task DockerFileTest()
{
using var projectDirectory = CopyTestProjectDirectory("dockerfile");
var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"));
var outputContext = new OutputContext(_sink, Verbosity.Debug);
var application = await ApplicationFactory.CreateAsync(outputContext, projectFile);
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
AllowAutoRedirect = false
};
var client = new HttpClient(new RetryHandler(handler));
await RunHostingApplication(application, new HostOptions(), async (app, uri) =>
{
var frontendUri = await GetServiceUrl(client, uri, "frontend");
var backendUri = await GetServiceUrl(client, uri, "backend");
var backendResponse = await client.GetAsync(backendUri);
var frontendResponse = await client.GetAsync(frontendUri);
Assert.True(backendResponse.IsSuccessStatusCode);
Assert.True(frontendResponse.IsSuccessStatusCode);
});
}
[ConditionalFact]
[SkipIfDockerNotRunning]
public async Task DockerFileChangeContextTest()
{
using var projectDirectory = CopyTestProjectDirectory("dockerfile");
File.Move(Path.Combine(projectDirectory.DirectoryPath, "backend", "Dockerfile"), Path.Combine(projectDirectory.DirectoryPath, "Dockerfile"));
var content = @"
name: frontend-backend
services:
- name: backend
dockerFile: Dockerfile
dockerFileContext: backend/
bindings:
- containerPort: 80
protocol: http
- name: frontend
project: frontend/frontend.csproj";
var projectFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml");
await File.WriteAllTextAsync(projectFile, content);
var outputContext = new OutputContext(_sink, Verbosity.Debug);
var application = await ApplicationFactory.CreateAsync(outputContext, new FileInfo(projectFile));
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
AllowAutoRedirect = false
};
var client = new HttpClient(new RetryHandler(handler));
await RunHostingApplication(application, new HostOptions(), async (app, uri) =>
{
var frontendUri = await GetServiceUrl(client, uri, "frontend");
var backendUri = await GetServiceUrl(client, uri, "backend");
var backendResponse = await client.GetAsync(backendUri);
var frontendResponse = await client.GetAsync(frontendUri);
Assert.True(backendResponse.IsSuccessStatusCode);
Assert.True(frontendResponse.IsSuccessStatusCode);
});
}
private async Task<string> GetServiceUrl(HttpClient client, Uri uri, string serviceName)
{
var serviceResult = await client.GetStringAsync($"{uri}api/v1/services/{serviceName}");

18
test/E2ETest/testassets/projects/dockerfile/backend/Dockerfile

@ -0,0 +1,18 @@
FROM node:10
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
CMD [ "node", "app.js" ]

10
test/E2ETest/testassets/projects/dockerfile/backend/app.js

@ -0,0 +1,10 @@
var express = require('express');
var http = require('http');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
http.createServer(app).listen(80, function () {
console.log('Example app listening on port 80!');
});

374
test/E2ETest/testassets/projects/dockerfile/backend/package-lock.json

@ -0,0 +1,374 @@
{
"name": "backend",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

14
test/E2ETest/testassets/projects/dockerfile/backend/package.json

@ -0,0 +1,14 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
}
}

24
test/E2ETest/testassets/projects/dockerfile/frontend/Program.cs

@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Frontend
{
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>();
});
}
}

27
test/E2ETest/testassets/projects/dockerfile/frontend/Properties/launchSettings.json

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:16377",
"sslPort": 44392
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"frontend": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

82
test/E2ETest/testassets/projects/dockerfile/frontend/Startup.cs

@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Frontend
{
public class Startup
{
private readonly JsonSerializerOptions options = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
var uri = Configuration.GetServiceUri("backend")!;
logger.LogInformation("Backend URL: {BackendUrl}", uri);
var httpClient = new HttpClient()
{
BaseAddress = uri
};
endpoints.MapGet("/", async context =>
{
var response = await httpClient.GetStringAsync("/");
await context.Response.WriteAsync($"Frontend Listening IP: {context.Connection.LocalIpAddress}{Environment.NewLine}");
await context.Response.WriteAsync($"Frontend Hostname: {Dns.GetHostName()}{Environment.NewLine}");
await context.Response.WriteAsync($"EnvVar Configuration value: {Configuration["App:Value"]}{Environment.NewLine}");
await context.Response.WriteAsync(response);
});
endpoints.MapHealthChecks("/healthz");
});
}
class BackendInfo
{
public string IP { get; set; } = default!;
public string Hostname { get; set; } = default!;
}
}
}

9
test/E2ETest/testassets/projects/dockerfile/frontend/appsettings.Development.json

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

10
test/E2ETest/testassets/projects/dockerfile/frontend/appsettings.json

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

12
test/E2ETest/testassets/projects/dockerfile/frontend/frontend.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Frontend</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(TyeLibrariesPath)\Microsoft.Tye.Extensions.Configuration\Microsoft.Tye.Extensions.Configuration.csproj" />
</ItemGroup>
</Project>

11
test/E2ETest/testassets/projects/dockerfile/tye.yaml

@ -0,0 +1,11 @@
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
name: frontend-backend
services:
- name: backend
dockerFile: backend/Dockerfile
bindings:
- containerPort: 80
protocol: http
- name: frontend
project: frontend/frontend.csproj

3
test/Test.Infrastructure/SkipIfDockerNotRunningAttribute.cs

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Microsoft.Tye;
namespace Test.Infrastructure
@ -13,7 +14,7 @@ namespace Test.Infrastructure
public SkipIfDockerNotRunningAttribute()
{
// TODO Check performance of this.
IsMet = DockerDetector.Instance.IsDockerConnectedToDaemon.Value.GetAwaiter().GetResult();
IsMet = DockerDetector.Instance.IsDockerConnectedToDaemon.Value.GetAwaiter().GetResult() && !(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS")));
SkipReason = "Docker is not installed or running.";
}

Loading…
Cancel
Save