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));
                }
            }
        }
Exemple #4
0
        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);
        }
Exemple #5
0
        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);
        }
Exemple #6
0
 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);
     }
 }
Exemple #8
0
        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));
                }
            }
        }
Exemple #10
0
        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);
        }
Exemple #11
0
    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));
            }
        }
    }
Exemple #12
0
        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);
            }
        }
Exemple #13
0
        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));
            }
        }
Exemple #14
0
        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;
        }
Exemple #15
0
        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);
                }
            }
        }
Exemple #16
0
        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()));
                }
            }
        }
Exemple #17
0
        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));
                }
            }
        }
Exemple #18
0
 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)));
                }
            }
        }