コード例 #1
0
ファイル: ComputedBindings.cs プロジェクト: weirdyang/tye
 public SecretInputBinding(string name, string filename, ServiceBuilder service, BindingBuilder binding)
 {
     Name     = name;
     Filename = filename;
     Service  = service;
     Binding  = binding;
 }
コード例 #2
0
        private static void AddProbe(ServiceBuilder service, YamlMappingNode container, ProbeBuilder builder, string name)
        {
            var probe = new YamlMappingNode();

            container.Add(name, probe);

            if (builder.Http != null)
            {
                var builderHttp = builder.Http;
                var httpGet     = new YamlMappingNode();

                probe.Add("httpGet", httpGet);
                httpGet.Add("path", builderHttp.Path);

                if (builderHttp.Protocol != null)
                {
                    httpGet.Add("scheme", builderHttp.Protocol.ToUpper());
                }

                if (builderHttp.Port != null)
                {
                    httpGet.Add("port", builderHttp.Port.ToString() !);
                }
                else
                {
                    // If port is not given, we pull default port
                    var binding = service.Bindings.First(b => builderHttp.Protocol == null || b.Protocol == builderHttp.Protocol);
                    if (binding.Port != null)
                    {
                        httpGet.Add("port", binding.Port.Value.ToString());
                    }

                    if (builderHttp.Protocol == null && binding.Protocol != null)
                    {
                        httpGet.Add("scheme", binding.Protocol.ToUpper());
                    }
                }

                if (builderHttp.Headers.Count > 0)
                {
                    var headers = new YamlSequenceNode();
                    httpGet.Add("httpHeaders", headers);

                    foreach (var builderHeader in builderHttp.Headers)
                    {
                        var header = new YamlMappingNode();
                        header.Add("name", builderHeader.Key);
                        header.Add("value", builderHeader.Value.ToString() !);
                        headers.Add(header);
                    }
                }
            }

            probe.Add("initialDelaySeconds", builder.InitialDelay.ToString());
            probe.Add("periodSeconds", builder.Period.ToString() !);
            probe.Add("successThreshold", builder.SuccessThreshold.ToString() !);
            probe.Add("failureThreshold", builder.FailureThreshold.ToString() !);
        }
コード例 #3
0
        public async Task ExecuteAsync(ServiceBuilder service)
        {
            using var tracker = output.BeginStep($"Processing Service '{service.Name}'...");
            for (var i = 0; i < steps.Length; i++)
            {
                var step = steps[i];

                using var stepTracker = output.BeginStep(step.DisplayText);
                await step.ExecuteAsync(output, application, service);

                stepTracker.MarkComplete();
            }
            tracker.MarkComplete();
        }
コード例 #4
0
        private static void AddProbe(ServiceBuilder service, YamlMappingNode container, ProbeBuilder builder, string probeName)
        {
            var probe = new YamlMappingNode();

            container.Add(probeName, probe);

            if (builder.Http != null)
            {
                var builderHttp = builder.Http;
                var httpGet     = new YamlMappingNode();

                probe.Add("httpGet", httpGet);
                httpGet.Add("path", builderHttp.Path);

                if (builderHttp.Protocol != null)
                {
                    httpGet.Add("scheme", builderHttp.Protocol.ToUpper());
                }

                if (builderHttp.Port != null)
                {
                    httpGet.Add("port", builderHttp.Port.ToString() !);
                }
                else
                {
                    // If port is not given, we pull default port
                    var binding = service.Bindings.First(b => builderHttp.Protocol == null || b.Protocol == builderHttp.Protocol);
                    if (binding.Port != null)
                    {
                        httpGet.Add("port", binding.Port.Value.ToString());
                    }

                    if (builderHttp.Protocol == null && binding.Protocol != null)
                    {
                        httpGet.Add("scheme", binding.Protocol.ToUpper());
                    }
                }

                if (builderHttp.Headers.Count > 0)
                {
                    var headers = new YamlSequenceNode();
                    httpGet.Add("httpHeaders", headers);

                    foreach (var(name, value) in builderHttp.Headers)
                    {
                        var header = new YamlMappingNode
                        {
                            { "name", name },
コード例 #5
0
            protected bool SkipWithoutProject(OutputContext output, ServiceBuilder service, [MaybeNullWhen(returnValue: true)] out ProjectServiceBuilder project)
            {
                if (output is null)
                {
                    throw new ArgumentNullException(nameof(output));
                }

                if (service is null)
                {
                    throw new ArgumentNullException(nameof(service));
                }

                if (service is ProjectServiceBuilder p)
                {
                    project = p;
                    return(false);
                }

                output.WriteInfoLine($"Service '{service.Name}' does not have a project associated. Skipping.");
                project = default !;
コード例 #6
0
        public static void ApplyHelmChartDefaults(ApplicationBuilder application, ServiceBuilder service, ContainerInfo container, HelmChartStep chart)
        {
            if (application is null)
            {
                throw new ArgumentNullException(nameof(application));
            }

            if (service is null)
            {
                throw new ArgumentNullException(nameof(service));
            }

            if (container is null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            if (chart is null)
            {
                throw new ArgumentNullException(nameof(chart));
            }

            chart.ChartName ??= service.Name.ToLowerInvariant();
        }
コード例 #7
0
ファイル: BuildDockerImageStep.cs プロジェクト: weirdyang/tye
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (!await DockerDetector.Instance.IsDockerInstalled.Value)
            {
                throw new CommandException($"Cannot generate a docker image for '{service.Name}' because docker is not installed.");
            }

            if (!await DockerDetector.Instance.IsDockerConnectedToDaemon.Value)
            {
                throw new CommandException($"Cannot generate a docker image for '{service.Name}' because docker is not running.");
            }

            await DockerContainerBuilder.BuildContainerImageAsync(output, application, project, container);
        }
コード例 #8
0
 public abstract Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service);
コード例 #9
0
ファイル: CombineStep.cs プロジェクト: siddheshp/tye
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            // No need to do this computation for a non-project since we're not deploying it.
            if (!(service is ProjectServiceBuilder project))
            {
                return(Task.CompletedTask);
            }

            // Compute ASPNETCORE_URLS based on the bindings exposed by *this* project.
            foreach (var binding in service.Bindings)
            {
                if (binding.Protocol == null && binding.ConnectionString == null)
                {
                    binding.Protocol = "http";
                }

                if (binding.Port == null && binding.Protocol == "http")
                {
                    binding.Port = 80;
                }

                if (binding.Protocol == "http")
                {
                    var port = binding.Port ?? 80;
                    var urls = $"http://*{(port == 80 ? "" : (":" + port.ToString()))}";
                    project.EnvironmentVariables.Add(new EnvironmentVariableBuilder("ASPNETCORE_URLS")
                    {
                        Value = urls,
                    });
                    project.EnvironmentVariables.Add(new EnvironmentVariableBuilder("PORT")
                    {
                        Value = port.ToString(CultureInfo.InvariantCulture),
                    });
                    break;
                }
            }

            // Process bindings and turn them into environment variables and secrets. There's
            // some duplication with the code in m8s (Application.cs) for populating environments.
            //
            // service.Service.Bindings is the bindings OUT - this step computes bindings IN.
            var bindings = new ComputedBindings();

            service.Outputs.Add(bindings);

            foreach (var other in application.Services)
            {
                if (object.ReferenceEquals(service, other))
                {
                    continue;
                }


                foreach (var binding in other.Bindings)
                {
                    if (other is ProjectServiceBuilder)
                    {
                        // The other thing is a project, and will be deployed along with this
                        // service.
                        var configName =
                            (binding.Name is null || binding.Name == other.Name) ?
                            other.Name.ToUpperInvariant() :
                            $"{other.Name.ToUpperInvariant()}__{binding.Name.ToUpperInvariant()}";

                        if (!string.IsNullOrEmpty(binding.ConnectionString))
                        {
                            // Special case for connection strings
                            bindings.Bindings.Add(new EnvironmentVariableInputBinding($"CONNECTIONSTRINGS__{configName}", binding.ConnectionString));
                            continue;
                        }

                        if (binding.Protocol == "https")
                        {
                            // We skip https for now in deployment, because the E2E requires certificates
                            // and we haven't done those features yet.
                            continue;
                        }

                        if (binding.Protocol == null)
                        {
                            binding.Protocol = "http";
                        }

                        if (binding.Port == null && binding.Protocol == "http")
                        {
                            binding.Port = 80;
                        }

                        if (!string.IsNullOrEmpty(binding.Protocol))
                        {
                            bindings.Bindings.Add(new EnvironmentVariableInputBinding($"SERVICE__{configName}__PROTOCOL", binding.Protocol));
                        }

                        if (binding.Port != null)
                        {
                            bindings.Bindings.Add(new EnvironmentVariableInputBinding($"SERVICE__{configName}__PORT", binding.Port.Value.ToString()));
                        }

                        bindings.Bindings.Add(new EnvironmentVariableInputBinding($"SERVICE__{configName}__HOST", binding.Host ?? other.Name));
                    }
                    else
                    {
                        // The other service is not a project, so we'll use secrets.
                        if (!string.IsNullOrEmpty(binding.ConnectionString))
                        {
                            bindings.Bindings.Add(new SecretConnectionStringInputBinding(
                                                      name: $"binding-{Environment}-{(binding.Name == null ? other.Name.ToLowerInvariant() : (other.Name.ToLowerInvariant() + "-" + binding.Name.ToLowerInvariant()))}-secret",
                                                      other,
                                                      binding,
                                                      keyname: $"CONNECTIONSTRINGS__{(binding.Name == null ? other.Name.ToUpperInvariant() : (other.Name.ToUpperInvariant() + "__" + binding.Name.ToUpperInvariant()))}"));
                        }
                        else
                        {
                            bindings.Bindings.Add(new SecretUrlInputBinding(
                                                      name: $"binding-{Environment}-{(binding.Name == null ? other.Name.ToLowerInvariant() : (other.Name.ToLowerInvariant() + "-" + binding.Name.ToLowerInvariant()))}-secret",
                                                      other,
                                                      binding,
                                                      keynamebase: $"SERVICE__{(binding.Name == null ? other.Name.ToUpperInvariant() : (other.Name.ToUpperInvariant() + "__" + binding.Name.ToUpperInvariant()))}"));
                        }
                    }
                }
            }

            return(Task.CompletedTask);
        }
コード例 #10
0
ファイル: ValidateSecretStep.cs プロジェクト: akshay123/tye
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            var bindings = service.Outputs.OfType <ComputedBindings>().FirstOrDefault();

            if (bindings is null)
            {
                return;
            }

            foreach (var binding in bindings.Bindings)
            {
                if (binding is SecretInputBinding secretInputBinding)
                {
                    if (!Secrets.Add(secretInputBinding.Name))
                    {
                        output.WriteDebugLine($"Already validated secret '{secretInputBinding.Name}'.");
                        continue;
                    }

                    output.WriteDebugLine($"Validating secret '{secretInputBinding.Name}'.");

                    var config = KubernetesClientConfiguration.BuildDefaultConfig();

                    // Workaround for https://github.com/kubernetes-client/csharp/issues/372
                    var store = await KubernetesClientConfiguration.LoadKubeConfigAsync();

                    var context = store.Contexts.Where(c => c.Name == config.CurrentContext).FirstOrDefault();

                    // Use namespace of application, or current context, or 'default'
                    config.Namespace = application.Namespace;
                    config.Namespace ??= context?.ContextDetails?.Namespace ?? "default";

                    var kubernetes = new Kubernetes(config);

                    try
                    {
                        var result = await kubernetes.ReadNamespacedSecretWithHttpMessagesAsync(secretInputBinding.Name, config.Namespace);

                        output.WriteInfoLine($"Found existing secret '{secretInputBinding.Name}'.");
                        continue;
                    }
                    catch (HttpOperationException ex) when(ex.Response.StatusCode == HttpStatusCode.NotFound)
                    {
                        // The kubernetes client uses exceptions for 404s.
                    }
                    catch (Exception ex)
                    {
                        output.WriteDebugLine("Failed to query secret.");
                        output.WriteDebugLine(ex.ToString());
                        throw new CommandException("Unable connect to kubernetes.", ex);
                    }

                    if (Force)
                    {
                        output.WriteDebugLine("Skipping because force was specified.");
                        continue;
                    }

                    if (!Interactive && secretInputBinding is SecretConnectionStringInputBinding)
                    {
                        throw new CommandException(
                                  $"The secret '{secretInputBinding.Name}' used for service '{secretInputBinding.Service.Name}' is missing from the deployment environment. " +
                                  $"Rerun the command with --interactive to specify the value interactively, or with --force to skip validation. Alternatively " +
                                  $"use the following command to manually create the secret." + System.Environment.NewLine +
                                  $"kubectl create secret generic {secretInputBinding.Name} --namespace {config.Namespace} --from-literal=connectionstring=<value>");
                    }

                    if (!Interactive && secretInputBinding is SecretUrlInputBinding)
                    {
                        throw new CommandException(
                                  $"The secret '{secretInputBinding.Name}' used for service '{secretInputBinding.Service.Name}' is missing from the deployment environment. " +
                                  $"Rerun the command with --interactive to specify the value interactively, or with --force to skip validation. Alternatively " +
                                  $"use the following command to manually create the secret." + System.Environment.NewLine +
                                  $"kubectl create secret generic {secretInputBinding.Name} --namespace {config.Namespace} --from-literal=protocol=<value> --from-literal=host=<value> --from-literal=port=<value>");
                    }

                    V1Secret secret;
                    if (secretInputBinding is SecretConnectionStringInputBinding)
                    {
                        // If we get here then we should create the secret.
                        var text = output.Prompt($"Enter the connection string to use for service '{secretInputBinding.Service.Name}'", allowEmpty: true);
                        if (string.IsNullOrWhiteSpace(text))
                        {
                            output.WriteAlwaysLine($"Skipping creation of secret for '{secretInputBinding.Service.Name}'. This may prevent creation of pods until secrets are created.");
                            output.WriteAlwaysLine($"Manually create a secret with:");
                            output.WriteAlwaysLine($"kubectl create secret generic {secretInputBinding.Name} --namespace {config.Namespace} --from-literal=connectionstring=<value>");
                            continue;
                        }

                        secret = new V1Secret(type: "Opaque", stringData: new Dictionary <string, string>()
                        {
                            { "connectionstring", text },
                        });
                    }
                    else if (secretInputBinding is SecretUrlInputBinding)
                    {
                        // If we get here then we should create the secret.
                        string text;
                        Uri?   uri = null;
                        while (true)
                        {
                            text = output.Prompt($"Enter the URI to use for service '{secretInputBinding.Service.Name}'", allowEmpty: true);
                            if (string.IsNullOrEmpty(text))
                            {
                                break; // skip
                            }
                            else if (Uri.TryCreate(text, UriKind.Absolute, out uri))
                            {
                                break; // success
                            }

                            output.WriteAlwaysLine($"Invalid URI: '{text}'");
                        }

                        if (string.IsNullOrWhiteSpace(text))
                        {
                            output.WriteAlwaysLine($"Skipping creation of secret for '{secretInputBinding.Service.Name}'. This may prevent creation of pods until secrets are created.");
                            output.WriteAlwaysLine($"Manually create a secret with:");
                            output.WriteAlwaysLine($"kubectl create secret generic {secretInputBinding.Name} --namespace {config.Namespace} --from-literal=protocol=<value> --from-literal=host=<value> --from-literal=port=<value>");
                            continue;
                        }

                        secret = new V1Secret(type: "Opaque", stringData: new Dictionary <string, string>()
                        {
                            { "protocol", uri !.Scheme },
コード例 #11
0
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (container.UseMultiphaseDockerfile == false)
            {
                throw new CommandException("Generated Dockerfile workflow does not support single-phase Dockerfiles.");
            }

            container.UseMultiphaseDockerfile ??= true;

            var dockerFilePath = Path.Combine(project.ProjectFile.DirectoryName, "Dockerfile");

            if (File.Exists(dockerFilePath) && !Force)
            {
                throw new CommandException("'Dockerfile' already exists for project. use '--force' to overwrite.");
            }

            File.Delete(dockerFilePath);

            await DockerfileGenerator.WriteDockerfileAsync(output, application, project, container, dockerFilePath);

            output.WriteInfoLine($"Generated Dockerfile at '{dockerFilePath}'.");
        }
コード例 #12
0
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            var yaml = service.Outputs.OfType <IYamlManifestOutput>().ToArray();

            if (yaml.Length == 0)
            {
                output.WriteDebugLine($"No yaml manifests found for service '{service.Name}'. Skipping.");
                return;
            }

            if (!await KubectlDetector.Instance.IsKubectlInstalled.Value)
            {
                throw new CommandException($"Cannot apply manifests for '{service.Name}' because kubectl is not installed.");
            }

            if (!await KubectlDetector.Instance.IsKubectlConnectedToCluster.Value)
            {
                throw new CommandException($"Cannot apply manifests for '{service.Name}' because kubectl is not connected to a cluster.");
            }

            using var tempFile = TempFile.Create();
            output.WriteDebugLine($"Writing output to '{tempFile.FilePath}'.");

            {
                await using var stream = File.OpenWrite(tempFile.FilePath);
                await using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: -1, leaveOpen: true);
                var yamlStream = new YamlStream(yaml.Select(y => y.Yaml));
                yamlStream.Save(writer, assignAnchors: false);
            }

            // kubectl apply logic is implemented in the client in older versions of k8s. The capability
            // to get the same behavior in the server isn't present in every version that's relevant.
            //
            // https://kubernetes.io/docs/reference/using-api/api-concepts/#server-side-apply
            //
            output.WriteDebugLine("Running 'kubectl apply'.");
            output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"kubectl",
                $"apply -f \"{tempFile.FilePath}\"",
                System.Environment.CurrentDirectory,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'kubectl apply' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("'kubectl apply' failed.");
            }

            output.WriteInfoLine($"Deployed service '{service.Name}'.");
        }
コード例 #13
0
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            var chartDirectory = Path.Combine(project.ProjectFile.DirectoryName, "charts");

            if (Directory.Exists(chartDirectory) && !Force)
            {
                throw new CommandException("'charts' directory already exists for project. use '--force' to overwrite.");
            }
            else if (Directory.Exists(chartDirectory))
            {
                Directory.Delete(chartDirectory, recursive: true);
            }

            var chart = new HelmChartStep();
            await HelmChartGenerator.GenerateAsync(
                output,
                application,
                project,
                container,
                chart,
                new DirectoryInfo(chartDirectory));

            output.WriteInfoLine($"Generated Helm Chart at '{Path.Combine(chartDirectory, chart.ChartName)}'.");
        }
コード例 #14
0
ファイル: PushDockerImageStep.cs プロジェクト: weirdyang/tye
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var _))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var _))
            {
                return;
            }

            foreach (var image in service.Outputs.OfType <DockerImageOutput>())
            {
                await DockerPush.ExecuteAsync(output, image.ImageName, image.ImageTag);

                output.WriteInfoLine($"Pushed docker image: '{image.ImageName}:{image.ImageTag}'");
            }
        }
コード例 #15
0
ファイル: ValidateSecretStep.cs プロジェクト: weirdyang/tye
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            var bindings = service.Outputs.OfType <ComputedBindings>().FirstOrDefault();

            if (bindings is null)
            {
                return;
            }

            foreach (var binding in bindings.Bindings)
            {
                if (binding is SecretInputBinding secretInputBinding)
                {
                    if (!Secrets.Add(secretInputBinding.Name))
                    {
                        output.WriteDebugLine($"Already validated secret '{secretInputBinding.Name}'.");
                        continue;
                    }

                    output.WriteDebugLine($"Validating secret '{secretInputBinding.Name}'.");

                    var config = KubernetesClientConfiguration.BuildDefaultConfig();

                    // Workaround for https://github.com/kubernetes-client/csharp/issues/372
                    var store = await KubernetesClientConfiguration.LoadKubeConfigAsync();

                    var context = store.Contexts.Where(c => c.Name == config.CurrentContext).FirstOrDefault();
                    config.Namespace ??= context?.ContextDetails?.Namespace;

                    var kubernetes = new Kubernetes(config);

                    try
                    {
                        var result = await kubernetes.ReadNamespacedSecretWithHttpMessagesAsync(secretInputBinding.Name, config.Namespace ?? "default");

                        output.WriteInfoLine($"Found existing secret '{secretInputBinding.Name}'.");
                        continue;
                    }
                    catch (HttpOperationException ex) when(ex.Response.StatusCode == HttpStatusCode.NotFound)
                    {
                        // The kubernetes client uses exceptions for 404s.
                    }
                    catch (Exception ex)
                    {
                        output.WriteDebugLine("Failed to query secret.");
                        output.WriteDebugLine(ex.ToString());
                        throw new CommandException("Unable connect to kubernetes.", ex);
                    }

                    if (Force)
                    {
                        output.WriteDebugLine("Skipping because force was specified.");
                        continue;
                    }

                    if (!Interactive)
                    {
                        throw new CommandException(
                                  $"The secret '{secretInputBinding.Name}' used for service '{secretInputBinding.Service.Name}' is missing from the deployment environment. " +
                                  $"Rerun the command with --interactive to specify the value interactively, or with --force to skip validation. Alternatively " +
                                  $"use the following command to manually create the secret." + System.Environment.NewLine +
                                  $"kubectl create secret generic {secretInputBinding.Name} --from-literal=connectionstring=<value>");
                    }

                    // If we get here then we should create the secret.
                    var text = output.Prompt($"Enter the connection string to use for service '{secretInputBinding.Service.Name}'", allowEmpty: true);
                    if (string.IsNullOrWhiteSpace(text))
                    {
                        output.WriteAlways($"Skipping creation of secret for '{secretInputBinding.Service.Name}'. This may prevent creation of pods until secrets are created.");
                        output.WriteAlways($"Manually create a secret with:");
                        output.WriteAlways($"kubectl create secret generic {secretInputBinding.Name} --from-literal=connectionstring=<value>");
                        continue;
                    }

                    var secret = new V1Secret(type: "Opaque", stringData: new Dictionary <string, string>()
                    {
                        { "connectionstring", text },
                    });
                    secret.Metadata      = new V1ObjectMeta();
                    secret.Metadata.Name = secretInputBinding.Name;

                    output.WriteDebugLine($"Creating secret '{secret.Metadata.Name}'.");

                    try
                    {
                        await kubernetes.CreateNamespacedSecretWithHttpMessagesAsync(secret, config.Namespace ?? "default");

                        output.WriteInfoLine($"Created secret '{secret.Metadata.Name}'.");
                    }
                    catch (Exception ex)
                    {
                        output.WriteDebugLine("Failed to create secret.");
                        output.WriteDebugLine(ex.ToString());
                        throw new CommandException("Failed to create secret.", ex);
                    }
                }
            }

            var yaml = service.Outputs.OfType <IYamlManifestOutput>().ToArray();

            if (yaml.Length == 0)
            {
                output.WriteDebugLine($"No yaml manifests found for service '{service.Name}'. Skipping.");
                return;
            }

            using var tempFile = TempFile.Create();
            output.WriteDebugLine($"Writing output to '{tempFile.FilePath}'.");

            {
                await using var stream = File.OpenWrite(tempFile.FilePath);
                await using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: -1, leaveOpen: true);
                var yamlStream = new YamlStream(yaml.Select(y => y.Yaml));
                yamlStream.Save(writer, assignAnchors: false);
            }

            // kubectl apply logic is implemented in the client in older versions of k8s. The capability
            // to get the same behavior in the server isn't present in every version that's relevant.
            //
            // https://kubernetes.io/docs/reference/using-api/api-concepts/#server-side-apply
            //
            output.WriteDebugLine("Running 'kubectl apply'.");
            output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"kubectl",
                $"apply -f \"{tempFile.FilePath}\"",
                System.Environment.CurrentDirectory,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'kubectl apply' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("'kubectl apply' failed.");
            }

            output.WriteInfoLine($"Deployed service '{service.Name}'.");
        }
コード例 #16
0
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (container.UseMultiphaseDockerfile != false)
            {
                return;
            }

            // NOTE: we're intentionally not cleaning up here. It's the responsibility of whomever consumes
            // the publish output to do cleanup.
            var outputDirectory = TempDirectory.Create();

            output.WriteDebugLine("Running 'dotnet publish'.");
            output.WriteCommandLine("dotnet", $"publish \"{project.ProjectFile.FullName}\" -c Release -o \"{outputDirectory.DirectoryPath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"dotnet",
                $"publish \"{project.ProjectFile.FullName}\" -c Release -o \"{outputDirectory.DirectoryPath}\"",
                project.ProjectFile.DirectoryName,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'dotnet publish' exit code: {exitCode}");
            if (exitCode != 0)
            {
                outputDirectory.Dispose();
                throw new CommandException("'dotnet publish' failed.");
            }

            output.WriteDebugLine($"Created Publish Output: '{outputDirectory.DirectoryPath}'");
            service.Outputs.Add(new ProjectPublishOutput(outputDirectory.DirectoryInfo));
        }
コード例 #17
0
 public SecretUrlInputBinding(string name, ServiceBuilder service, BindingBuilder binding, string keynamebase)
     : base(name, service, binding)
 {
     KeyNameBase = keynamebase;
 }
コード例 #18
0
 public SecretConnectionStringInputBinding(string name, ServiceBuilder service, BindingBuilder binding, string keyname)
     : base(name, service, binding)
 {
     KeyName = keyname;
 }
コード例 #19
0
 protected SecretInputBinding(string name, ServiceBuilder service, BindingBuilder binding)
 {
     Name    = name;
     Service = service;
     Binding = binding;
 }
コード例 #20
0
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutContainerOutput(output, service))
            {
                return(Task.CompletedTask);
            }

            if (SkipWithoutProject(output, service, out var project))
            {
                return(Task.CompletedTask);
            }

            var deployment = project.ManifestInfo?.Deployment;

            if (deployment is null)
            {
                return(Task.CompletedTask);
            }

            // Initialize defaults for deployment-related settings
            deployment.Labels.TryAdd("app.kubernetes.io/name", project.Name);
            deployment.Labels.TryAdd("app.kubernetes.io/part-of", application.Name);

            service.Outputs.Add(KubernetesManifestGenerator.CreateDeployment(output, application, project, deployment));

            if (service.Bindings.Count > 0 &&
                project.ManifestInfo?.Service is ServiceManifestInfo k8sService)
            {
                // Initialize defaults for service-related settings
                k8sService.Labels.TryAdd("app.kubernetes.io/name", project.Name);
                k8sService.Labels.TryAdd("app.kubernetes.io/part-of", application.Name);

                service.Outputs.Add(KubernetesManifestGenerator.CreateService(output, application, project, deployment, k8sService));
            }

            return(Task.CompletedTask);
        }
コード例 #21
0
        public static ServiceOutput CreateService(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (application is null)
            {
                throw new ArgumentNullException(nameof(application));
            }

            if (service is null)
            {
                throw new ArgumentNullException(nameof(service));
            }

            var root = new YamlMappingNode();

            root.Add("kind", "Service");
            root.Add("apiVersion", "v1");

            var metadata = new YamlMappingNode();

            root.Add("metadata", metadata);
            metadata.Add("name", service.Name);

            var labels = new YamlMappingNode();

            metadata.Add("labels", labels);
            labels.Add("app.kubernetes.io/name", service.Name);

            labels.Add("app.kubernetes.io/part-of", application.Name);

            var spec = new YamlMappingNode();

            root.Add("spec", spec);

            var selector = new YamlMappingNode();

            spec.Add("selector", selector);
            selector.Add("app.kubernetes.io/name", service.Name);

            spec.Add("type", "ClusterIP");

            var ports = new YamlSequenceNode();

            spec.Add("ports", ports);

            // We figure out the port based on bindings
            foreach (var binding in service.Bindings)
            {
                if (binding.Protocol == "https")
                {
                    // We skip https for now in deployment, because the E2E requires certificates
                    // and we haven't done those features yet.
                    continue;
                }

                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.Port.Value.ToString());
                }
            }

            return(new KubernetesServiceOutput(service.Name, new YamlDocument(root)));
        }
コード例 #22
0
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (container.UseMultiphaseDockerfile != false)
            {
                return;
            }

            var outputDirectory = Path.Combine(project.ProjectFile.DirectoryName, "bin", "Release", project.TargetFramework, "publish");

            output.WriteDebugLine("Running 'dotnet publish'.");
            output.WriteCommandLine("dotnet", $"publish \"{project.ProjectFile.FullName}\" -c Release -o \"{outputDirectory}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"dotnet",
                $"publish \"{project.ProjectFile.FullName}\" -c Release -o \"{outputDirectory}\"",
                project.ProjectFile.DirectoryName,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'dotnet publish' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("'dotnet publish' failed.");
            }

            output.WriteInfoLine($"Created Publish Output: '{outputDirectory}'");
            service.Outputs.Add(new ProjectPublishOutput(new DirectoryInfo(outputDirectory)));
        }
コード例 #23
0
ファイル: BuildDockerImageStep.cs プロジェクト: spboyer/tye
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (!application.ContainerEngine.IsUsable(out string?unusableReason))
            {
                throw new CommandException($"Cannot generate a docker image for '{service.Name}' because {unusableReason}.");
            }

            if (project is DotnetProjectServiceBuilder dotnetProject)
            {
                await DockerContainerBuilder.BuildContainerImageAsync(output, application, dotnetProject, container);
            }
            else if (project is DockerFileServiceBuilder dockerFile)
            {
                await DockerContainerBuilder.BuildContainerImageFromDockerFileAsync(output, application, dockerFile, container);
            }
        }
コード例 #24
0
        public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (container.UseMultiphaseDockerfile != false)
            {
                return;
            }

            // NOTE: we're intentionally not cleaning up here. It's the responsibility of whomever consumes
            // the publish output to do cleanup.
            var outputDirectory = TempDirectory.Create();

            output.WriteDebugLine("Running 'dotnet publish'.");
            output.WriteCommandLine("dotnet", $"publish \"{project.ProjectFile.FullName}\" -c Release -o \"{outputDirectory.DirectoryPath}\"");

            var publishResult = await ProcessUtil.RunAsync(
                $"dotnet",
                $"publish \"{project.ProjectFile.FullName}\" -c Release -o \"{outputDirectory.DirectoryPath}\"",
                project.ProjectFile.DirectoryName,
                throwOnError : false);

            output.WriteDebugLine($"Done running 'dotnet publish' exit code: {publishResult.ExitCode}");
            if (publishResult.ExitCode != 0)
            {
                outputDirectory.Dispose();
                output.WriteInfoLine($"'dotnet publish' failed. Error:");

                foreach (var line in publishResult.StandardOutput.Split(Environment.NewLine))
                {
                    output.WriteInfoLine(line);
                }

                foreach (var line in publishResult.StandardError.Split(Environment.NewLine))
                {
                    output.WriteInfoLine(line);
                }

                throw new CommandException("'dotnet publish' failed.");
            }

            output.WriteDebugLine($"Created Publish Output: '{outputDirectory.DirectoryPath}'");
            service.Outputs.Add(new ProjectPublishOutput(outputDirectory.DirectoryInfo));
        }
コード例 #25
0
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutContainerOutput(output, service))
            {
                return(Task.CompletedTask);
            }

            if (SkipWithoutProject(output, service, out var project))
            {
                return(Task.CompletedTask);
            }

            service.Outputs.Add(KubernetesManifestGenerator.CreateDeployment(output, application, project));

            if (service.Bindings.Count > 0)
            {
                service.Outputs.Add(KubernetesManifestGenerator.CreateService(output, application, project));
            }

            return(Task.CompletedTask);
        }
コード例 #26
0
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutContainerOutput(output, service))
            {
                return(Task.CompletedTask);
            }

            if (SkipWithoutProject(output, service, out var project))
            {
                return(Task.CompletedTask);
            }

            var component = OamComponentGenerator.CreateOamComponent(output, application, project);

            service.Outputs.Add(component);
            return(Task.CompletedTask);
        }
コード例 #27
0
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return(Task.CompletedTask);
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return(Task.CompletedTask);
            }

            if (project is DotnetProjectServiceBuilder dotnetProject)
            {
                DockerfileGenerator.ApplyContainerDefaults(application, dotnetProject, container);
            }
            else if (project is DockerFileServiceBuilder dockerFile)
            {
                DockerfileGenerator.ApplyContainerDefaults(application, dockerFile, container);
            }
            return(Task.CompletedTask);
        }
コード例 #28
0
ファイル: CombineStep.cs プロジェクト: zggl/tye
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            // No need to do this computation for a non-project since we're not deploying it.
            if (!(service is ProjectServiceBuilder project))
            {
                return(Task.CompletedTask);
            }

            // Compute ASPNETCORE_URLS based on the bindings exposed by *this* project.
            foreach (var binding in service.Bindings)
            {
                if (binding.Protocol == null && binding.ConnectionString == null)
                {
                    binding.Protocol = "http";
                }

                if (binding.Port == null && binding.Protocol == "http")
                {
                    binding.Port = 80;
                }

                if (binding.Protocol == "http")
                {
                    var port = binding.Port ?? 80;
                    var urls = $"http://*{(port == 80 ? "" : (":" + port.ToString()))}";
                    project.EnvironmentVariables.Add(new EnvironmentVariableBuilder("ASPNETCORE_URLS")
                    {
                        Value = urls,
                    });
                    project.EnvironmentVariables.Add(new EnvironmentVariableBuilder("PORT")
                    {
                        Value = port.ToString(CultureInfo.InvariantCulture),
                    });
                    break;
                }
            }

            // Process bindings and turn them into environment variables and secrets. There's
            // some duplication with the code in m8s (Application.cs) for populating environments.
            //
            // service.Service.Bindings is the bindings OUT - this step computes bindings IN.
            service.Outputs.Add(ComputeBindings(application, service.Dependencies));

            foreach (var sidecar in project.Sidecars)
            {
                sidecar.Outputs.Add(ComputeBindings(application, sidecar.Dependencies));
            }

            return(Task.CompletedTask);
        }
コード例 #29
0
        public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
        {
            var yaml = service.Outputs.OfType <IYamlManifestOutput>().ToArray();

            if (yaml.Length == 0)
            {
                output.WriteDebugLine($"No yaml manifests found for service '{service.Name}'. Skipping.");
                return(Task.CompletedTask);
            }

            var outputFilePath = Path.Combine(OutputDirectory.FullName, $"{service.Name}.yaml");

            output.WriteInfoLine($"Writing output to '{outputFilePath}'.");
            if (File.Exists(outputFilePath) && !Force)
            {
                throw new CommandException($"'{service.Name}.yaml' already exists for project. use '--force' to overwrite.");
            }

            File.Delete(outputFilePath);

            using var stream = File.OpenWrite(outputFilePath);
            using var writer = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), bufferSize: -1, leaveOpen: true);
            var yamlStream = new YamlStream(yaml.Select(y => y.Yaml));

            yamlStream.Save(writer, assignAnchors: false);

            return(Task.CompletedTask);
        }