Skip to content
This repository has been archived by the owner on Nov 20, 2023. It is now read-only.

Commit

Permalink
Tye should validate that service names are valid DNS names (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkotalik authored May 15, 2020
1 parent 32ce5b7 commit ef441e8
Show file tree
Hide file tree
Showing 19 changed files with 168 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.dotnet/
artifacts/
.tye/

**/.vscode/

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
Expand Down
7 changes: 6 additions & 1 deletion docs/reference/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,12 @@ services:

#### `name` (string) *required*

The service name. Each service must have a name, and it must be a legal DNS name: (`a-z` + `_`).
The service name. Each service must have a name, and it must be a legal DNS name: (`a-z` + `-`). Specifically, the service name must:

- Contain at most 63 characters
- Contain only alphanumeric characters or ‘-’
- Start with an alphanumeric character
- End with an alphanumeric character

#### `project` (string)

Expand Down
3 changes: 1 addition & 2 deletions samples/apps-with-ingress/tye.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ ingress:
- host: a.example.com
service: app-a
- host: b.example.com
service: app-b

service: app-b
services:
- name: app-a
project: ApplicationA/ApplicationA.csproj
Expand Down
8 changes: 6 additions & 2 deletions src/Microsoft.Tye.Core/ConfigModel/ConfigFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.Tye.Serialization;
using Tye.Serialization;

Expand Down Expand Up @@ -42,7 +43,7 @@ private static ConfigApplication FromProject(FileInfo file)

var service = new ConfigService()
{
Name = Path.GetFileNameWithoutExtension(file.Name).ToLowerInvariant(),
Name = NormalizeServiceName(Path.GetFileNameWithoutExtension(file.Name)),
Project = file.FullName.Replace('\\', '/'),
};

Expand Down Expand Up @@ -73,7 +74,7 @@ private static ConfigApplication FromSolution(FileInfo file)
{
var service = new ConfigService()
{
Name = Path.GetFileNameWithoutExtension(projectFile.Name).ToLowerInvariant(),
Name = NormalizeServiceName(Path.GetFileNameWithoutExtension(projectFile.Name)),
Project = projectFile.FullName.Replace('\\', '/'),
};

Expand All @@ -97,5 +98,8 @@ private static ConfigApplication FromYaml(FileInfo file)
using var parser = new YamlParser(file);
return parser.ParseConfigApplication();
}

private static string NormalizeServiceName(string name)
=> Regex.Replace(name.ToLowerInvariant(), "[^0-9A-Za-z-]+", "-");
}
}
8 changes: 8 additions & 0 deletions src/Microsoft.Tye.Core/ConfigModel/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Tye;
using YamlDotNet.Serialization;

namespace Microsoft.Tye.ConfigModel
{
public class ConfigService
{
const string ErrorMessage = "A service name must consist of lower case alphanumeric characters or '-'," +
" start with an alphabetic character, and end with an alphanumeric character" +
" (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?').";
const string MaxLengthErrorMessage = "Name cannot be more that 63 characters long.";

[Required]
[RegularExpression("[a-z]([-a-z0-9]*[a-z0-9])?", ErrorMessage = ErrorMessage)]
[MaxLength(63, ErrorMessage = MaxLengthErrorMessage)]
public string Name { get; set; } = default!;
public bool External { get; set; }
public string? Image { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.Tye.Core/Serialization/ConfigIngressParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private static void HandleIngressMapping(YamlMappingNode yamlMappingNode, Config
switch (key)
{
case "name":
configIngress.Name = YamlParser.GetScalarValue(key, child.Value);
configIngress.Name = YamlParser.GetScalarValue(key, child.Value).ToLowerInvariant();
break;
case "replicas":
if (!int.TryParse(YamlParser.GetScalarValue(key, child.Value), out var replicas))
Expand Down Expand Up @@ -91,7 +91,7 @@ private static void HandleIngressRuleMapping(YamlMappingNode yamlMappingNode, Co
rule.Path = YamlParser.GetScalarValue(key, child.Value);
break;
case "service":
rule.Service = YamlParser.GetScalarValue(key, child.Value);
rule.Service = YamlParser.GetScalarValue(key, child.Value).ToLowerInvariant();
break;
default:
throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private static void HandleServiceNameMapping(YamlMappingNode yamlMappingNode, Co
switch (key)
{
case "name":
service.Name = YamlParser.GetScalarValue(key, child.Value);
service.Name = YamlParser.GetScalarValue(key, child.Value).ToLowerInvariant();
break;
case "external":
if (!bool.TryParse(YamlParser.GetScalarValue(key, child.Value), out var external))
Expand Down
14 changes: 7 additions & 7 deletions test/E2ETest/TyeGenerateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public async Task GenerateWorksWithoutRegistry()
public async Task Generate_DaprApplication()
{
var applicationName = "dapr_test_application";
var projectName = "dapr_test_project";
var projectName = "dapr-test-project";
var environment = "production";

await DockerAssert.DeleteDockerImagesAsync(output, projectName);
Expand Down Expand Up @@ -363,8 +363,8 @@ public async Task Generate_Ingress()
var applicationName = "apps-with-ingress";
var environment = "production";

await DockerAssert.DeleteDockerImagesAsync(output, "app-a");
await DockerAssert.DeleteDockerImagesAsync(output, "app-b");
await DockerAssert.DeleteDockerImagesAsync(output, "appa");
await DockerAssert.DeleteDockerImagesAsync(output, "appa");

using var projectDirectory = TestHelpers.CopyTestProjectDirectory(applicationName);

Expand All @@ -383,13 +383,13 @@ public async Task Generate_Ingress()

YamlAssert.Equals(expectedContent, content, output);

await DockerAssert.AssertImageExistsAsync(output, "app-a");
await DockerAssert.AssertImageExistsAsync(output, "app-b");
await DockerAssert.AssertImageExistsAsync(output, "appa");
await DockerAssert.AssertImageExistsAsync(output, "appb");
}
finally
{
await DockerAssert.DeleteDockerImagesAsync(output, "app-a");
await DockerAssert.DeleteDockerImagesAsync(output, "app-b");
await DockerAssert.DeleteDockerImagesAsync(output, "appa");
await DockerAssert.DeleteDockerImagesAsync(output, "appb");
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions test/E2ETest/TyeInitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,20 @@ public void Init_ProjectKinds()

YamlAssert.Equals(expectedContent, content);
}


[Fact]
public void Console_Normalization_Service_Name()
{
using var projectDirectory = CopyTestProjectDirectory("Console.Normalization.svc.Name");

var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "Console.Normalization.svc.Name.csproj"));

var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false);

var expectedContent = File.ReadAllText("testassets/init/console-normalization-svc-name.yaml");

YamlAssert.Equals(expectedContent, content);
}
}
}
8 changes: 4 additions & 4 deletions test/E2ETest/TyeRunTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ public async Task IngressRunTest()
await RunHostingApplication(application, new HostOptions(), async (app, uri) =>
{
var ingressUri = await GetServiceUrl(client, uri, "ingress");
var appAUri = await GetServiceUrl(client, uri, "app-a");
var appBUri = await GetServiceUrl(client, uri, "app-b");
var appAUri = await GetServiceUrl(client, uri, "appa");
var appBUri = await GetServiceUrl(client, uri, "appb");

var appAResponse = await client.GetAsync(appAUri);
var appBResponse = await client.GetAsync(appBUri);
Expand Down Expand Up @@ -538,8 +538,8 @@ public async Task NginxIngressTest()
await RunHostingApplication(application, new HostOptions(), async (app, uri) =>
{
var nginxUri = await GetServiceUrl(client, uri, "nginx");
var appAUri = await GetServiceUrl(client, uri, "appA");
var appBUri = await GetServiceUrl(client, uri, "appB");
var appAUri = await GetServiceUrl(client, uri, "appa");
var appBUri = await GetServiceUrl(client, uri, "appb");

var nginxResponse = await client.GetAsync(nginxUri);
var appAResponse = await client.GetAsync(appAUri);
Expand Down
60 changes: 30 additions & 30 deletions test/E2ETest/testassets/generate/apps-with-ingress.yaml
Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: app-a
name: appa
labels:
app.kubernetes.io/name: 'app-a'
app.kubernetes.io/name: 'appa'
app.kubernetes.io/part-of: 'apps-with-ingress'
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: app-a
app.kubernetes.io/name: appa
template:
metadata:
labels:
app.kubernetes.io/name: 'app-a'
app.kubernetes.io/name: 'appa'
app.kubernetes.io/part-of: 'apps-with-ingress'
spec:
containers:
- name: app-a
image: app-a:1.0.0
- name: appa
image: appa:1.0.0
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: 'http://*'
- name: PORT
value: '80'
- name: SERVICE__APP-B__PROTOCOL
- name: SERVICE__APPB__PROTOCOL
value: 'http'
- name: SERVICE__APP-B__PORT
- name: SERVICE__APPB__PORT
value: '80'
- name: SERVICE__APP-B__HOST
value: 'app-b'
- name: SERVICE__APPB__HOST
value: 'appb'
ports:
- containerPort: 80
...
---
kind: Service
apiVersion: v1
metadata:
name: app-a
name: appa
labels:
app.kubernetes.io/name: 'app-a'
app.kubernetes.io/name: 'appa'
app.kubernetes.io/part-of: 'apps-with-ingress'
spec:
selector:
app.kubernetes.io/name: app-a
app.kubernetes.io/name: appa
type: ClusterIP
ports:
- name: http
Expand All @@ -56,50 +56,50 @@ spec:
kind: Deployment
apiVersion: apps/v1
metadata:
name: app-b
name: appb
labels:
app.kubernetes.io/name: 'app-b'
app.kubernetes.io/name: 'appb'
app.kubernetes.io/part-of: 'apps-with-ingress'
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: app-b
app.kubernetes.io/name: appb
template:
metadata:
labels:
app.kubernetes.io/name: 'app-b'
app.kubernetes.io/name: 'appb'
app.kubernetes.io/part-of: 'apps-with-ingress'
spec:
containers:
- name: app-b
image: app-b:1.0.0
- name: appb
image: appb:1.0.0
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: 'http://*'
- name: PORT
value: '80'
- name: SERVICE__APP-A__PROTOCOL
- name: SERVICE__APPA__PROTOCOL
value: 'http'
- name: SERVICE__APP-A__PORT
- name: SERVICE__APPA__PORT
value: '80'
- name: SERVICE__APP-A__HOST
value: 'app-a'
- name: SERVICE__APPA__HOST
value: 'appa'
ports:
- containerPort: 80
...
---
kind: Service
apiVersion: v1
metadata:
name: app-b
name: appb
labels:
app.kubernetes.io/name: 'app-b'
app.kubernetes.io/name: 'appb'
app.kubernetes.io/part-of: 'apps-with-ingress'
spec:
selector:
app.kubernetes.io/name: app-b
app.kubernetes.io/name: appb
type: ClusterIP
ports:
- name: http
Expand All @@ -122,25 +122,25 @@ spec:
- http:
paths:
- backend:
serviceName: app-a
serviceName: appa
servicePort: 80
path: /A(/|$)(.*)
- backend:
serviceName: app-b
serviceName: appb
servicePort: 80
path: /B(/|$)(.*)
- host: a.example.com
http:
paths:
- backend:
serviceName: app-a
serviceName: appa
servicePort: 80
path: /()(.*)
- host: b.example.com
http:
paths:
- backend:
serviceName: app-b
serviceName: appb
servicePort: 80
path: /()(.*)
...
Loading

0 comments on commit ef441e8

Please sign in to comment.
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy