@ -42,71 +42,73 @@ namespace Microsoft.Tye
var spec = new YamlMappingNode ( ) ;
root . Add ( "spec" , spec ) ;
if ( ingress . Rules . Count > 0 )
if ( ingress . Rules . Count = = 0 )
{
var rules = new YamlSequenceNode ( ) ;
spec . Add ( "rules" , rules ) ;
return new KubernetesIngressOutput ( ingress . Name , new YamlDocument ( root ) ) ;
}
// k8s ingress is grouped by host first, then grouped by path
foreach ( var hostgroup in ingress . Rules . GroupBy ( r = > r . Host ) )
{
var rule = new YamlMappingNode ( ) ;
rules . Add ( rule ) ;
var rules = new YamlSequenceNode ( ) ;
spec . Add ( "rules" , rules ) ;
if ( ! string . IsNullOrEmpty ( hostgroup . Key ) )
{
rule . Add ( "host" , hostgroup . Key ) ;
}
// k8s ingress is grouped by host first, then grouped by path
foreach ( var hostgroup in ingress . Rules . GroupBy ( r = > r . Host ) )
{
var rule = new YamlMappingNode ( ) ;
rules . Add ( rule ) ;
var http = new YamlMappingNode ( ) ;
rule . Add ( "http" , http ) ;
if ( ! string . IsNullOrEmpty ( hostgroup . Key ) )
{
rule . Add ( "host" , hostgroup . Key ) ;
}
var paths = new YamlSequenceNode ( ) ;
http . Add ( "paths " , paths ) ;
var htt p = new YamlMapping Node ( ) ;
rule . Add ( "htt p" , htt p) ;
foreach ( var ingressRule in hostgroup )
{
var path = new YamlMappingNode ( ) ;
paths . Add ( path ) ;
var paths = new YamlSequenceNode ( ) ;
http . Add ( "paths" , paths ) ;
var backend = new YamlMappingNode ( ) ;
path . Add ( "backend" , backend ) ;
backend . Add ( "serviceName" , ingressRule . Service ) ;
foreach ( var ingressRule in hostgroup )
{
var path = new YamlMappingNode ( ) ;
paths . Add ( path ) ;
var service = application . Services . FirstOrDefault ( s = > s . Name = = ingressRule . Service ) ;
if ( service is null )
{
throw new InvalidOperationException ( $"Could not resolve service '{ingressRule.Service}'." ) ;
}
var backend = new YamlMappingNode ( ) ;
path . Add ( "backend" , backend ) ;
backend . Add ( "serviceName" , ingressRule . Service ) ;
var binding = service . Binding s . FirstOrDefault ( b = > b . Name is null | | b . Name = = "http" ) ;
if ( binding is null )
{
throw new InvalidOperationException ( $"Could not resolve an http binding for service '{service.Nam e}'." ) ;
}
var service = application . Services . FirstOrDefault ( s = > s . Name = = ingressRule . Service ) ;
if ( service is null )
{
throw new InvalidOperationException ( $"Could not resolve service '{ingre ssRule.S ervice}'." ) ;
}
backend . Add ( "servicePort" , ( binding . Port ? ? 8 0 ) . ToString ( CultureInfo . InvariantCulture ) ) ;
var binding = service . Bindings . FirstOrDefault ( b = > b . Name is null | | b . Name = = "http" ) ;
if ( binding is null )
{
throw new InvalidOperationException ( $"Could not resolve an http binding for service '{service.Name}'." ) ;
}
// Tye implements path matching similar to this example:
// https://kubernetes.github.io/ingress-nginx/examples/rewrite/
//
// Therefore our rewrite-target is set to $2 - we want to make sure we have
// two capture groups.
if ( string . IsNullOrEmpty ( ingressRule . Path ) | | ingressRule . Path = = "/" )
backend . Add ( "servicePort" , ( binding . Port ? ? 8 0 ) . ToString ( CultureInfo . InvariantCulture ) ) ;
// Tye implements path matching similar to this example:
// https://kubernetes.github.io/ingress-nginx/examples/rewrite/
//
// Therefore our rewrite-target is set to $2 - we want to make sure we have
// two capture groups.
if ( string . IsNullOrEmpty ( ingressRule . Path ) | | ingressRule . Path = = "/" )
{
path . Add ( "path" , "/()(.*)" ) ; // () is an empty capture group.
}
else
{
if ( ingressRule . PreservePath )
{
path . Add ( "path" , "/()(.*)" ) ; // () is an empty capture group.
path . Add ( "path" , $ "/()({ingressRule.Path.Trim('/')} .*)") ;
}
else
{
if ( ingressRule . PreservePath )
{
path . Add ( "path" , $"/()({ingressRule.Path.Trim('/')}.*)" ) ;
}
else
{
var regex = $"{ingressRule.Path.TrimEnd('/')}(/|$)(.*)" ;
path . Add ( "path" , regex ) ;
}
var regex = $"{ingressRule.Path.TrimEnd('/')}(/|$)(.*)" ;
path . Add ( "path" , regex ) ;
}
}
}
@ -182,16 +184,18 @@ namespace Microsoft.Tye
continue ;
}
if ( binding . Port ! = null )
if ( binding . Port = = null )
{
var port = new YamlMappingNode ( ) ;
ports . Add ( port ) ;
port . Add ( "name" , binding . Name ? ? binding . Protocol ? ? "http" ) ;
port . Add ( "protocol" , "TCP" ) ; // we use assume TCP. YOLO
port . Add ( "port" , binding . Port . Value . ToString ( ) ) ;
port . Add ( "targetPort" , ( binding . ContainerPort ? ? binding . Port . Value ) . ToString ( ) ) ;
continue ;
}
var port = new YamlMappingNode ( ) ;
ports . Add ( port ) ;
port . Add ( "name" , binding . Name ? ? binding . Protocol ? ? "http" ) ;
port . Add ( "protocol" , "TCP" ) ; // we use assume TCP. YOLO
port . Add ( "port" , binding . Port . Value . ToString ( ) ) ;
port . Add ( "targetPort" , ( binding . ContainerPort ? ? binding . Port . Value ) . ToString ( ) ) ;
}
return new KubernetesServiceOutput ( project . Name , new YamlDocument ( root ) ) ;
@ -205,10 +209,11 @@ namespace Microsoft.Tye
{
var bindings = project . Outputs . OfType < ComputedBindings > ( ) . FirstOrDefault ( ) ;
var root = new YamlMappingNode ( ) ;
root . Add ( "kind" , "Deployment" ) ;
root . Add ( "apiVersion" , "apps/v1" ) ;
var root = new YamlMappingNode
{
{ "kind" , "Deployment" } ,
{ "apiVersion" , "apps/v1" }
} ;
var metadata = new YamlMappingNode ( ) ;
root . Add ( "metadata" , metadata ) ;
@ -343,7 +348,7 @@ namespace Microsoft.Tye
} ) ;
}
if ( bindings is object )
if ( bindings ! = null )
{
AddEnvironmentVariablesForComputedBindings ( env , bindings ) ;
}
@ -377,12 +382,14 @@ namespace Microsoft.Tye
continue ;
}
if ( binding . Port ! = null )
if ( binding . Port = = null )
{
var containerPort = new YamlMappingNode ( ) ;
ports . Add ( containerPort ) ;
containerPort . Add ( "containerPort" , ( binding . ContainerPort ? ? binding . Port . Value ) . ToString ( ) ) ;
continue ;
}
var containerPort = new YamlMappingNode ( ) ;
ports . Add ( containerPort ) ;
containerPort . Add ( "containerPort" , ( binding . ContainerPort ? ? binding . Port . Value ) . ToString ( ) ) ;
}
}
@ -431,48 +438,51 @@ namespace Microsoft.Tye
} ) ;
}
if ( sidecarBindings is object )
if ( sidecarBindings ! = null )
{
AddEnvironmentVariablesForComputedBindings ( env , sidecarBindings ) ;
}
}
if ( project . RelocateDiagnosticsDomainSockets )
if ( ! project . RelocateDiagnosticsDomainSockets )
{
// volumeMounts:
// - name: shared-data
// mountPath: /usr/share/nginx/html
var volumeMounts = new YamlSequenceNode ( ) ;
container . Add ( "volumeMounts" , volumeMounts ) ;
var volumeMount = new YamlMappingNode ( ) ;
volumeMounts . Add ( volumeMount ) ;
volumeMount . Add ( "name" , "tye-diagnostics" ) ;
volumeMount . Add ( "mountPath" , "/var/tye/diagnostics" ) ;
continue ;
}
// volumeMounts:
// - name: shared-data
// mountPath: /usr/share/nginx/html
var volumeMounts = new YamlSequenceNode ( ) ;
container . Add ( "volumeMounts" , volumeMounts ) ;
var volumeMount = new YamlMappingNode ( ) ;
volumeMounts . Add ( volumeMount ) ;
volumeMount . Add ( "name" , "tye-diagnostics" ) ;
volumeMount . Add ( "mountPath" , "/var/tye/diagnostics" ) ;
}
if ( project . RelocateDiagnosticsDomainSockets )
if ( ! project . RelocateDiagnosticsDomainSockets )
{
// volumes:
// - name: shared-data
// emptyDir: {}
var volumes = new YamlSequenceNode ( ) ;
spec . Add ( "volumes" , volumes ) ;
var volume = new YamlMappingNode ( ) ;
volumes . Add ( volume ) ;
volume . Add ( "name" , "tye-diagnostics" ) ;
volume . Add ( "emptyDir" , new YamlMappingNode ( ) ) ;
return new KubernetesDeploymentOutput ( project . Name , new YamlDocument ( root ) ) ;
}
// volumes:
// - name: shared-data
// emptyDir: {}
var volumes = new YamlSequenceNode ( ) ;
spec . Add ( "volumes" , volumes ) ;
var volume = new YamlMappingNode ( ) ;
volumes . Add ( volume ) ;
volume . Add ( "name" , "tye-diagnostics" ) ;
volume . Add ( "emptyDir" , new YamlMappingNode ( ) ) ;
return new KubernetesDeploymentOutput ( project . Name , new YamlDocument ( root ) ) ;
}
private static void AddProbe ( ServiceBuilder service , YamlMappingNode container , ProbeBuilder builder , string name )
private static void AddProbe ( ServiceBuilder service , YamlMappingNode container , ProbeBuilder builder , string probeN ame)
{
var probe = new YamlMappingNode ( ) ;
container . Add ( name , probe ) ;
container . Add ( probeN ame, probe ) ;
if ( builder . Http ! = null )
{
@ -511,11 +521,13 @@ namespace Microsoft.Tye
var headers = new YamlSequenceNode ( ) ;
httpGet . Add ( "httpHeaders" , headers ) ;
foreach ( var builderHeader in builderHttp . Headers )
foreach ( var ( name , value ) in builderHttp . Headers )
{
var header = new YamlMappingNode ( ) ;
header . Add ( "name" , builderHeader . Key ) ;
header . Add ( "value" , builderHeader . Value . ToString ( ) ! ) ;
var header = new YamlMappingNode
{
{ "name" , name } ,
{ "value" , value . ToString ( ) ! }
} ;
headers . Add ( header ) ;
}
}
@ -531,7 +543,7 @@ namespace Microsoft.Tye
{
foreach ( var binding in bindings . Bindings . OfType < EnvironmentVariableInputBinding > ( ) )
{
env . Add ( new YamlMappingNode ( )
env . Add ( new YamlMappingNode
{
{ "name" , binding . Name } ,
{ "value" , new YamlScalarNode ( binding . Value ) { Style = ScalarStyle . SingleQuoted , } } ,
@ -546,16 +558,16 @@ namespace Microsoft.Tye
// name: mysecret
// key: username
if ( binding is SecretConnectionStringInputBinding connectionStringBinding )
{
AddSecret ( env , connectionStringBinding . KeyName , binding . Name , "connectionstring" ) ;
}
else if ( binding is SecretUrlInputBinding urlBinding )
switch ( binding )
{
AddSecret ( env , $"{urlBinding.KeyNameBase}__PROTOCOL" , binding . Name , "protocol" ) ;
AddSecret ( env , $"{urlBinding.KeyNameBase}__HOST" , binding . Name , "host" ) ;
AddSecret ( env , $"{urlBinding.KeyNameBase}__PORT" , binding . Name , "port" ) ;
case SecretConnectionStringInputBinding connectionStringBinding :
AddSecret ( env , connectionStringBinding . KeyName , binding . Name , "connectionstring" ) ;
break ;
case SecretUrlInputBinding urlBinding :
AddSecret ( env , $"{urlBinding.KeyNameBase}__PROTOCOL" , binding . Name , "protocol" ) ;
AddSecret ( env , $"{urlBinding.KeyNameBase}__HOST" , binding . Name , "host" ) ;
AddSecret ( env , $"{urlBinding.KeyNameBase}__PORT" , binding . Name , "port" ) ;
break ;
}
}