public static void HandleConfigApplication(YamlMappingNode yamlMappingNode, ConfigApplication app) { foreach (var child in yamlMappingNode.Children) { var key = YamlParser.GetScalarValue(child.Key); switch (key) { case "name": app.Name = YamlParser.GetScalarValue(key, child.Value); break; case "namespace": app.Namespace = YamlParser.GetScalarValue(key, child.Value); break; case "network": app.Network = YamlParser.GetScalarValue(key, child.Value); break; case "registry": app.Registry = YamlParser.GetScalarValue(key, child.Value); break; case "containerEngine": string engine = YamlParser.GetScalarValue(key, child.Value); if (engine.Equals("docker", StringComparison.InvariantCultureIgnoreCase)) { app.ContainerEngineType = ContainerEngineType.Docker; } else if (engine.Equals("podman", StringComparison.InvariantCultureIgnoreCase)) { app.ContainerEngineType = ContainerEngineType.Podman; } else { throw new TyeYamlException($"Unknown container engine: \"{engine}\""); } break; case "ingress": YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigIngressParser.HandleIngress((child.Value as YamlSequenceNode) !, app.Ingress); break; case "services": YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigServiceParser.HandleServiceMapping((child.Value as YamlSequenceNode) !, app.Services, app); break; case "extensions": YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigExtensionsParser.HandleExtensionsMapping((child.Value as YamlSequenceNode) !, app.Extensions); break; default: throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key)); } } }
private static void ParseVolumes(YamlMappingNode node, ConfigApplication app) { foreach (var child in node.Children) { var key = YamlParser.GetScalarValue(child.Key); switch (key) { case "driver": break; case "driver_opts": break; case "external": break; case "labels": break; case "name": break; default: throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key)); } } }
internal static void Parse(YamlMappingNode node, ConfigApplication app) { foreach (var child in node.Children) { var key = YamlParser.GetScalarValue(child.Key); switch (key) { case "version": break; case "volumes": break; case "services": ParseServiceList((child.Value as YamlMappingNode) !, app); break; case "networks": break; case "configs": break; case "secrets": break; default: throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key)); } } }
private static async Task ExecuteDeployAsync(OutputContext output, ConfigApplication application, string environment, bool interactive, bool force) { if (!await KubectlDetector.Instance.IsKubectlInstalled.Value) { throw new CommandException($"Cannot apply manifests because kubectl is not installed."); } if (!await KubectlDetector.Instance.IsKubectlConnectedToCluster.Value) { throw new CommandException($"Cannot apply manifests because kubectl is not connected to a cluster."); } var temporaryApplication = await CreateApplicationAdapterAsync(output, application, interactive, requireRegistry: true); var steps = new List<ServiceExecutor.Step>() { new CombineStep() { Environment = environment, }, new PublishProjectStep(), new BuildDockerImageStep() { Environment = environment, }, new PushDockerImageStep() { Environment = environment, }, new ValidateSecretStep() { Environment = environment, Interactive = interactive, Force = force, }, }; steps.Add(new GenerateKubernetesManifestStep() { Environment = environment, }); steps.Add(new DeployServiceYamlStep() { Environment = environment, }); var executor = new ServiceExecutor(output, temporaryApplication, steps); foreach (var service in temporaryApplication.Services) { await executor.ExecuteAsync(service); } await DeployApplicationManifestAsync(output, temporaryApplication, application.Source.Directory.Name, environment); }
public static async Task ExecuteGenerateAsync(OutputContext output, ConfigApplication application, string environment, bool interactive) { var temporaryApplication = await Program.CreateApplicationAdapterAsync(output, application, interactive, requireRegistry : false); var steps = new List <ServiceExecutor.Step>() { new CombineStep() { Environment = environment, }, new PublishProjectStep(), new BuildDockerImageStep() { Environment = environment, }, // Make an image but don't push it }; steps.Add(new GenerateKubernetesManifestStep() { Environment = environment, }); var executor = new ServiceExecutor(output, temporaryApplication, steps); foreach (var service in temporaryApplication.Services) { await executor.ExecuteAsync(service); } await GenerateApplicationManifestAsync(output, temporaryApplication, application.Name ?? application.Source.Directory.Name, environment); }
public TemporaryApplicationAdapter( ConfigApplication application, ApplicationGlobals globals, IReadOnlyList <ServiceEntry> services) { this.application = application; Globals = globals; Services = services; }
private static void ParseServiceList(YamlMappingNode node, ConfigApplication app) { foreach (var child in node.Children) { var service = new ConfigService(); service.Name = YamlParser.GetScalarValue(child.Key); ParseService((child.Value as YamlMappingNode) !, service); app.Services.Add(service); } }
public CustomViewEngine() : base() { List <string> viewRoutes = new List <string>(); ConfigApplication.GetSchemas().ForEach(f => viewRoutes.Add($"~/Views/{f}/{{1}}/{{0}}.cshtml")); //string[] schemas = new string[] { "Basic" , "CRM"}; //foreach (string schema in schemas) //{ // viewRoutes.Add($"~/Views/{schema}/{{1}}/{{0}}.cshtml"); //} ViewLocationFormats = PartialViewLocationFormats = viewRoutes.ToArray(); }
public static void HandleConfigApplication(YamlMappingNode yamlMappingNode, ConfigApplication app) { foreach (var child in yamlMappingNode.Children) { var key = YamlParser.GetScalarValue(child.Key); switch (key) { case "name": app.Name = YamlParser.GetScalarValue(key, child.Value); break; case "namespace": app.Namespace = YamlParser.GetScalarValue(key, child.Value); break; case "network": app.Network = YamlParser.GetScalarValue(key, child.Value); break; case "registry": app.Registry = YamlParser.GetScalarValue(key, child.Value); break; case "ingress": YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigIngressParser.HandleIngress((child.Value as YamlSequenceNode) !, app.Ingress); break; case "services": YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigServiceParser.HandleServiceMapping((child.Value as YamlSequenceNode) !, app.Services, app); break; case "extensions": YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigExtensionsParser.HandleExtensionsMapping((child.Value as YamlSequenceNode) !, app.Extensions); break; default: throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key)); } } }
public ConfigApplication ParseConfigApplication() { try { _yamlStream.Load(_reader); } catch (YamlException ex) { throw new TyeYamlException(ex.Start, "Unable to parse tye.yaml. See inner exception.", ex); } var app = new ConfigApplication(); // TODO assuming first document. var document = _yamlStream.Documents[0]; var node = document.RootNode; ThrowIfNotYamlMapping(node); app.Source = _fileInfo !; ConfigApplicationParser.HandleConfigApplication((YamlMappingNode)node, app); app.Name ??= NameInferer.InferApplicationName(_fileInfo !); // TODO confirm if these are ever null. foreach (var service in app.Services) { service.Bindings ??= new List <ConfigServiceBinding>(); service.Configuration ??= new List <ConfigConfigurationSource>(); service.Volumes ??= new List <ConfigVolume>(); service.Tags ??= new List <string>(); } foreach (var ingress in app.Ingress) { ingress.Bindings ??= new List <ConfigIngressBinding>(); ingress.Rules ??= new List <ConfigIngressRule>(); ingress.Tags ??= new List <string>(); } return(app); }
public static void HandleConfigApplication(YamlMappingNode yamlMappingNode, ConfigApplication app) { foreach (var child in yamlMappingNode.Children) { var key = YamlParser.GetScalarValue(child.Key); switch (key) { case "name": app.Name = YamlParser.GetScalarValue(key, child.Value); break; case "services": // YamlParser.ThrowIfNotYamlSequence(key, child.Value); ConfigServiceParser.HandleServiceMapping((child.Value as YamlSequenceNode) !, app.Services); break; default: break; // throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key)); } } }
public static async Task ExecuteBuildAsync(OutputContext output, ConfigApplication application, string environment, bool interactive) { var temporaryApplication = await Program.CreateApplicationAdapterAsync(output, application, interactive, requireRegistry : false); var steps = new List <ServiceExecutor.Step>() { new CombineStep() { Environment = environment, }, new PublishProjectStep(), new BuildDockerImageStep() { Environment = environment, }, }; var executor = new ServiceExecutor(output, temporaryApplication, steps); foreach (var service in temporaryApplication.Services) { await executor.ExecuteAsync(service); } }
public static async Task ExecuteUndeployAsync(OutputContext output, ConfigApplication application, string @namespace, bool interactive, bool whatIf) { var config = KubernetesClientConfiguration.BuildDefaultConfig(); var kubernetes = new Kubernetes(config); // If namespace is null, set it to default config.Namespace ??= "default"; // Due to some limitations in the k8s SDK we currently have a hardcoded list of resource // types that we handle deletes for. If we start adding extensibility for the *kinds* of // k8s resources we create, or the ability to deploy additional files along with the // resources we understand then we should revisit this. // // Basically the challenges are: // // - kubectl api-resources --all (and similar) are implemented client-side (n+1 problem) // - the C# k8s SDK doesn't have an untyped api for operations on arbitrary resources, the // closest thing is the custom resource APIs // - Legacy resources without an api group don't follow the same URL scheme as more modern // ones, and thus cannot be addressed using the custom resource APIs. // // So solving 'undeploy' generically would involve doing a bunch of work to query things // generically, including going outside of what's provided by the SDK. // // - querying api-resources // - querying api-groups // - handcrafing requests to list for each resource // - handcrafting requests to delete each resource var resources = new List <Resource>(); var applicationName = application.Name; try { output.WriteDebugLine("Querying services"); var response = await kubernetes.ListNamespacedServiceWithHttpMessagesAsync( config.Namespace, labelSelector : $"app.kubernetes.io/part-of={applicationName}"); foreach (var resource in response.Body.Items) { resource.Kind = V1Service.KubeKind; } resources.AddRange(response.Body.Items.Select(item => new Resource(item.Kind, item.Metadata, DeleteService))); output.WriteDebugLine($"Found {response.Body.Items.Count} matching services"); } catch (Exception ex) { output.WriteDebugLine("Failed to query services."); output.WriteDebugLine(ex.ToString()); throw new CommandException("Unable connect to kubernetes.", ex); } try { output.WriteDebugLine("Querying deployments"); var response = await kubernetes.ListNamespacedDeploymentWithHttpMessagesAsync( config.Namespace, labelSelector : $"app.kubernetes.io/part-of={applicationName}"); foreach (var resource in response.Body.Items) { resource.Kind = V1Deployment.KubeKind; } resources.AddRange(response.Body.Items.Select(item => new Resource(item.Kind, item.Metadata, DeleteDeployment))); output.WriteDebugLine($"Found {response.Body.Items.Count} matching deployments"); } catch (Exception ex) { output.WriteDebugLine("Failed to query deployments."); output.WriteDebugLine(ex.ToString()); throw new CommandException("Unable connect to kubernetes.", ex); } try { output.WriteDebugLine("Querying secrets"); var response = await kubernetes.ListNamespacedSecretWithHttpMessagesAsync( config.Namespace, labelSelector : $"app.kubernetes.io/part-of={applicationName}"); foreach (var resource in response.Body.Items) { resource.Kind = V1Secret.KubeKind; } resources.AddRange(response.Body.Items.Select(item => new Resource(item.Kind, item.Metadata, DeleteSecret))); output.WriteDebugLine($"Found {response.Body.Items.Count} matching secrets"); } catch (Exception ex) { output.WriteDebugLine("Failed to query secrets."); output.WriteDebugLine(ex.ToString()); throw new CommandException("Unable connect to kubernetes.", ex); } try { output.WriteDebugLine("Querying ingresses"); var response = await kubernetes.ListNamespacedIngressWithHttpMessagesAsync( config.Namespace, labelSelector : $"app.kubernetes.io/part-of={applicationName}"); foreach (var resource in response.Body.Items) { resource.Kind = "Ingress"; } resources.AddRange(response.Body.Items.Select(item => new Resource(item.Kind, item.Metadata, DeleteIngress))); output.WriteDebugLine($"Found {response.Body.Items.Count} matching ingress"); } catch (Exception ex) { output.WriteDebugLine("Failed to query ingress."); output.WriteDebugLine(ex.ToString()); throw new CommandException("Unable connect to kubernetes.", ex); } output.WriteInfoLine($"Found {resources.Count} resource(s)."); var exceptions = new List <(Resource resource, HttpOperationException exception)>(); foreach (var resource in resources) { var operation = Operations.Delete; if (interactive && !output.Confirm($"Delete {resource.Kind} '{resource.Metadata.Name}'?")) { operation = Operations.None; } if (whatIf && operation == Operations.Delete) { operation = Operations.Explain; } if (operation == Operations.None) { output.WriteAlwaysLine($"Skipping '{resource.Kind}' '{resource.Metadata.Name}' ..."); } else if (operation == Operations.Explain) { output.WriteAlwaysLine($"whatif: Deleting '{resource.Kind}' '{resource.Metadata.Name}' ..."); } else if (operation == Operations.Delete) { output.WriteAlwaysLine($"Deleting '{resource.Kind}' '{resource.Metadata.Name}' ..."); try { var response = await resource.Deleter(resource.Metadata.Name); output.WriteDebugLine($"Successfully deleted resource: '{resource.Kind}' '{resource.Metadata.Name}'."); } catch (HttpOperationException ex) { output.WriteDebugLine($"Failed to delete resource: '{resource.Kind}' '{resource.Metadata.Name}'."); output.WriteDebugLine(ex.ToString()); exceptions.Add((resource, ex)); } } } if (exceptions.Count > 0) { throw new CommandException( $"Failed to delete some resources: " + Environment.NewLine + Environment.NewLine + string.Join(Environment.NewLine, exceptions.Select(e => $"\t'{e.resource.Kind}' '{e.resource.Metadata.Name}': {e.exception.Body}."))); } Task <Rest.HttpOperationResponse <V1Status> > DeleteService(string name) { return(kubernetes !.DeleteNamespacedServiceWithHttpMessagesAsync(name, config !.Namespace)); } Task <Rest.HttpOperationResponse <V1Status> > DeleteDeployment(string name) { return(kubernetes !.DeleteNamespacedDeploymentWithHttpMessagesAsync(name, config !.Namespace)); } Task <Rest.HttpOperationResponse <V1Status> > DeleteSecret(string name) { return(kubernetes !.DeleteNamespacedSecretWithHttpMessagesAsync(name, config !.Namespace)); } Task <Rest.HttpOperationResponse <V1Status> > DeleteIngress(string name) { return(kubernetes !.DeleteNamespacedIngressWithHttpMessagesAsync(name, config !.Namespace)); } }
internal static async Task<TemporaryApplicationAdapter> CreateApplicationAdapterAsync(OutputContext output, ConfigApplication application, bool interactive, bool requireRegistry) { var globals = new ApplicationGlobals() { Name = application.Name, Registry = application.Registry is null ? null : new ContainerRegistry(application.Registry), }; var services = new List<Tye.ServiceEntry>(); foreach (var configService in application.Services) { if (configService.Project is string projectFile) { var fullPathProjectFile = Path.Combine(application.Source.DirectoryName, projectFile); var project = new Project(fullPathProjectFile); var service = new Service(configService.Name) { Source = project, }; service.Replicas = configService.Replicas ?? 1; foreach (var configBinding in configService.Bindings) { var binding = new ServiceBinding(configBinding.Name ?? service.Name) { ConnectionString = configBinding.ConnectionString, Host = configBinding.Host, Port = configBinding.Port, Protocol = configBinding.Protocol, }; binding.Protocol ??= "http"; if (binding.Port == null && configBinding.AutoAssignPort) { if (binding.Protocol == "http" || binding.Protocol == null) { binding.Port = 80; } else if (binding.Protocol == "https") { binding.Port = 443; } } service.Bindings.Add(binding); } var serviceEntry = new ServiceEntry(service, configService.Name); await ProjectReader.ReadProjectDetailsAsync(output, new FileInfo(fullPathProjectFile), project); var container = new ContainerInfo() { UseMultiphaseDockerfile = false, }; service.GeneratedAssets.Container = container; services.Add(serviceEntry); } else { // For a non-project, we don't really need much info about it, just the name and bindings var service = new Service(configService.Name); foreach (var configBinding in configService.Bindings) { service.Bindings.Add(new ServiceBinding(configBinding.Name ?? service.Name) { ConnectionString = configBinding.ConnectionString, Host = configBinding.Host, Port = configBinding.Port, Protocol = configBinding.Protocol, }); } var serviceEntry = new ServiceEntry(service, configService.Name); services.Add(serviceEntry); } } var temporaryApplication = new TemporaryApplicationAdapter(application, globals, services); if (temporaryApplication.Globals.Registry?.Hostname == null && interactive) { var registry = output.Prompt("Enter the Container Registry (ex: 'example.azurecr.io' for Azure or 'example' for dockerhub)", allowEmpty: !requireRegistry); if (!string.IsNullOrWhiteSpace(registry)) { temporaryApplication.Globals.Registry = new ContainerRegistry(registry.Trim()); } } else if (temporaryApplication.Globals.Registry?.Hostname == null && requireRegistry) { throw new CommandException("A registry is required for deploy operations. Add the registry to 'tye.yaml' or use '-i' for interactive mode."); } else { // No registry specified, and that's OK! } foreach (var service in temporaryApplication.Services) { if (service.Service.Source is Project project && service.Service.GeneratedAssets.Container is ContainerInfo container) { DockerfileGenerator.ApplyContainerDefaults(temporaryApplication, service, project, container); } } return temporaryApplication; }
public static void Equal(ConfigApplication expected, ConfigApplication actual) { Assert.Equal(expected.Name, actual.Name); Assert.Equal(expected.Registry, actual.Registry); Assert.Equal(expected.Network, actual.Network); foreach (var ingress in actual.Ingress) { var otherIngress = expected .Ingress .Where(o => o.Name == ingress.Name) .Single(); Assert.NotNull(otherIngress); Assert.Equal(otherIngress.Replicas, ingress.Replicas); foreach (var rule in ingress.Rules) { var otherRule = otherIngress .Rules .Where(o => o.Path == rule.Path && o.Host == rule.Host && o.Service == rule.Service) .Single(); Assert.NotNull(otherRule); } foreach (var binding in ingress.Bindings) { var otherBinding = otherIngress .Bindings .Where(o => o.Name == binding.Name && o.Port == binding.Port && o.Protocol == binding.Protocol) .Single(); Assert.NotNull(otherBinding); } } foreach (var service in actual.Services) { var otherService = expected .Services .Where(o => o.Name == service.Name) .Single(); Assert.NotNull(otherService); Assert.Equal(otherService.Args, service.Args); Assert.Equal(otherService.Build, service.Build); Assert.Equal(otherService.Executable, service.Executable); Assert.Equal(otherService.External, service.External); Assert.Equal(otherService.Image, service.Image); Assert.Equal(otherService.Project, service.Project); Assert.Equal(otherService.Replicas, service.Replicas); Assert.Equal(otherService.WorkingDirectory, service.WorkingDirectory); foreach (var binding in service.Bindings) { var otherBinding = otherService.Bindings .Where(o => o.Name == binding.Name && o.Port == binding.Port && o.Protocol == binding.Protocol && o.ConnectionString == binding.ConnectionString && o.ContainerPort == binding.ContainerPort && o.Host == binding.Host) .Single(); Assert.NotNull(otherBinding); } foreach (var binding in service.Bindings) { var otherBinding = otherService.Bindings .Where(o => o.Name == binding.Name && o.Port == binding.Port && o.Protocol == binding.Protocol && o.ConnectionString == binding.ConnectionString && o.ContainerPort == binding.ContainerPort && o.Host == binding.Host) .Single(); Assert.NotNull(otherBinding); } foreach (var config in service.Configuration) { var otherConfig = otherService.Configuration .Where(o => o.Name == config.Name && o.Value == config.Value) .Single(); Assert.NotNull(otherConfig); } foreach (var volume in service.Volumes) { var otherVolume = otherService.Volumes .Where(o => o.Name == volume.Name && o.Target == volume.Target && o.Source == volume.Source) .Single(); Assert.NotNull(otherVolume); } } }
private static void HandleServiceEnvFiles(YamlSequenceNode yamlSequenceNode, List <ConfigConfigurationSource> configuration, ConfigApplication application) { foreach (var child in yamlSequenceNode.Children) { switch (child) { case YamlScalarNode childScalarNode: var envFile = new FileInfo(Path.Combine(application.Source?.DirectoryName ?? Directory.GetCurrentDirectory(), YamlParser.GetScalarValue(childScalarNode))); if (!envFile.Exists) { throw new TyeYamlException(child.Start, CoreStrings.FormatPathNotFound(envFile.FullName)); } HandleServiceEnvFile(childScalarNode, File.ReadAllLines(envFile.FullName), configuration); break; default: throw new TyeYamlException(child.Start, CoreStrings.FormatUnexpectedType(YamlNodeType.Scalar.ToString(), child.NodeType.ToString())); } } }
private static void HandleServiceNameMapping(YamlMappingNode yamlMappingNode, ConfigService service, ConfigApplication application) { foreach (var child in yamlMappingNode !.Children) { var key = YamlParser.GetScalarValue(child.Key); switch (key) { case "name": service.Name = YamlParser.GetScalarValue(key, child.Value).ToLowerInvariant(); break; case "external": if (!bool.TryParse(YamlParser.GetScalarValue(key, child.Value), out var external)) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatMustBeABoolean(key)); } service.External = external; break; case "image": service.Image = YamlParser.GetScalarValue(key, child.Value); break; case "dockerFile": service.DockerFile = YamlParser.GetScalarValue(key, child.Value); break; case "dockerFileArgs": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleDockerFileArgs((child.Value as YamlSequenceNode) !, service.DockerFileArgs); break; case "dockerFileContext": service.DockerFileContext = YamlParser.GetScalarValue(key, child.Value); break; case "project": service.Project = YamlParser.GetScalarValue(key, child.Value); break; case "buildProperties": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleBuildProperties((child.Value as YamlSequenceNode) !, service.BuildProperties); break; case "include": service.Include = YamlParser.GetScalarValue(key, child.Value); break; case "repository": service.Repository = YamlParser.GetScalarValue(key, child.Value); break; case "build": if (!bool.TryParse(YamlParser.GetScalarValue(key, child.Value), out var build)) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatMustBeABoolean(key)); } service.Build = build; break; case "executable": service.Executable = YamlParser.GetScalarValue(key, child.Value); break; case "workingDirectory": service.WorkingDirectory = YamlParser.GetScalarValue(key, child.Value); break; case "args": service.Args = YamlParser.GetScalarValue(key, child.Value); break; case "replicas": if (!int.TryParse(YamlParser.GetScalarValue(key, child.Value), out var replicas)) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatMustBeAnInteger(key)); } if (replicas < 0) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatMustBePositive(key)); } service.Replicas = replicas; break; case "bindings": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleServiceBindings((child.Value as YamlSequenceNode) !, service.Bindings); break; case "volumes": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleServiceVolumes((child.Value as YamlSequenceNode) !, service.Volumes); break; case "env": case "configuration": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleServiceConfiguration((child.Value as YamlSequenceNode) !, service.Configuration); break; case "env_file": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleServiceEnvFiles((child.Value as YamlSequenceNode) !, service.Configuration, application); break; case "liveness": service.Liveness = new ConfigProbe(); HandleServiceProbe((YamlMappingNode)child.Value, service.Liveness !); break; case "readiness": service.Readiness = new ConfigProbe(); HandleServiceProbe((YamlMappingNode)child.Value, service.Readiness !); break; case "tags": if (child.Value.NodeType != YamlNodeType.Sequence) { throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key)); } HandleServiceTags((child.Value as YamlSequenceNode) !, service.Tags); break; case "azureFunction": service.AzureFunction = YamlParser.GetScalarValue(key, child.Value); break; case "pathToFunc": service.FuncExecutable = YamlParser.GetScalarValue(key, child.Value); break; case "cloneDirectory": service.CloneDirectory = YamlParser.GetScalarValue(key, child.Value); break; default: throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key)); } } }
public static void HandleServiceMapping(YamlSequenceNode yamlSequenceNode, List <ConfigService> services, ConfigApplication application) { foreach (var child in yamlSequenceNode.Children) { YamlParser.ThrowIfNotYamlMapping(child); var service = new ConfigService(); HandleServiceNameMapping((YamlMappingNode)child, service, application); services.Add(service); } }
private static void ValidateConfigApplication(ConfigApplication config) { var context = new ValidationContext(config); var results = new List <ValidationResult>(); if (!Validator.TryValidateObject(config, context, results, validateAllProperties: true)) { throw new CommandException( "Configuration validation failed." + Environment.NewLine + string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage))); } foreach (var service in config.Services) { context = new ValidationContext(service); if (!Validator.TryValidateObject(service, context, results, validateAllProperties: true)) { throw new CommandException( $"Service '{service.Name}' validation failed." + Environment.NewLine + string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage))); } foreach (var binding in service.Bindings) { context = new ValidationContext(binding); if (!Validator.TryValidateObject(binding, context, results, validateAllProperties: true)) { throw new CommandException( $"Binding '{binding.Name}' of service '{service.Name}' validation failed." + Environment.NewLine + string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage))); } } foreach (var envVar in service.Configuration) { context = new ValidationContext(service); if (!Validator.TryValidateObject(service, context, results, validateAllProperties: true)) { throw new CommandException( $"Environment variable '{envVar.Name}' of service '{service.Name}' validation failed." + Environment.NewLine + string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage))); } } foreach (var volume in service.Volumes) { context = new ValidationContext(service); if (!Validator.TryValidateObject(service, context, results, validateAllProperties: true)) { throw new CommandException( $"Volume '{volume.Source}' of service '{service.Name}' validation failed." + Environment.NewLine + string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage))); } } } foreach (var ingress in config.Ingress) { // We don't currently recurse into ingress rules or ingress bindings right now. // There's nothing to validate there. context = new ValidationContext(ingress); if (!Validator.TryValidateObject(ingress, context, results, validateAllProperties: true)) { throw new CommandException( $"Ingress '{ingress.Name}' validation failed." + Environment.NewLine + string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage))); } } }