Beispiel #1
0
        public static KubernetesServiceOutput CreateService(
            OutputContext output,
            ApplicationBuilder application,
            ProjectServiceBuilder project,
            DeploymentManifestInfo deployment,
            ServiceManifestInfo service)
        {
            var root = new YamlMappingNode();

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

            var metadata = new YamlMappingNode();

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

            if (!string.IsNullOrEmpty(application.Namespace))
            {
                metadata.Add("namespace", application.Namespace);
            }

            if (service.Annotations.Count > 0)
            {
                var annotations = new YamlMappingNode();
                metadata.Add("annotations", annotations);

                foreach (var annotation in service.Annotations)
                {
                    annotations.Add(annotation.Key, new YamlScalarNode(annotation.Value)
                    {
                        Style = ScalarStyle.SingleQuoted,
                    });
                }
            }

            var labels = new YamlMappingNode();

            metadata.Add("labels", labels);
            foreach (var label in service.Labels)
            {
                labels.Add(label.Key, new YamlScalarNode(label.Value)
                {
                    Style = ScalarStyle.SingleQuoted,
                });
            }

            var spec = new YamlMappingNode();

            root.Add("spec", spec);

            var selector = new YamlMappingNode();

            spec.Add("selector", selector);

            // We need the name so we can use it with selector.
            if (!deployment.Labels.TryGetValue("app.kubernetes.io/name", out var selectorLabelValue))
            {
                throw new InvalidOperationException("The label 'app.kubernetes.io/name` is required.");
            }
            selector.Add("app.kubernetes.io/name", selectorLabelValue);

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

            var ports = new YamlSequenceNode();

            spec.Add("ports", ports);

            // We figure out the port based on bindings
            foreach (var binding in project.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(project.Name, new YamlDocument(root)));
        }
Beispiel #2
0
        public static KubernetesDeploymentOutput CreateDeployment(
            OutputContext output,
            ApplicationBuilder application,
            ProjectServiceBuilder project,
            DeploymentManifestInfo deployment)
        {
            var bindings = project.Outputs.OfType <ComputedBindings>().FirstOrDefault();

            var root = new YamlMappingNode();

            root.Add("kind", "Deployment");
            root.Add("apiVersion", "apps/v1");

            var metadata = new YamlMappingNode();

            root.Add("metadata", metadata);
            metadata.Add("name", project.Name);
            if (!string.IsNullOrEmpty(application.Namespace))
            {
                metadata.Add("namespace", application.Namespace);
            }

            if (deployment.Annotations.Count > 0)
            {
                var annotations = new YamlMappingNode();
                metadata.Add("annotations", annotations);

                foreach (var annotation in deployment.Annotations)
                {
                    annotations.Add(annotation.Key, new YamlScalarNode(annotation.Value)
                    {
                        Style = ScalarStyle.SingleQuoted,
                    });
                }
            }

            var labels = new YamlMappingNode();

            metadata.Add("labels", labels);
            foreach (var label in deployment.Labels)
            {
                labels.Add(label.Key, new YamlScalarNode(label.Value)
                {
                    Style = ScalarStyle.SingleQuoted,
                });
            }

            var spec = new YamlMappingNode();

            root.Add("spec", spec);

            spec.Add("replicas", project.Replicas.ToString());

            var selector = new YamlMappingNode();

            spec.Add("selector", selector);

            var matchLabels = new YamlMappingNode();

            selector.Add("matchLabels", matchLabels);

            // We need the name so we can use it with matchLabels.
            if (!deployment.Labels.TryGetValue("app.kubernetes.io/name", out var matchLabelsLabelValue))
            {
                throw new InvalidOperationException("The label 'app.kubernetes.io/name` is required.");
            }
            matchLabels.Add("app.kubernetes.io/name", matchLabelsLabelValue);

            var template = new YamlMappingNode();

            spec.Add("template", template);

            metadata = new YamlMappingNode();
            template.Add("metadata", metadata);

            if (deployment.Annotations.Count > 0)
            {
                var annotations = new YamlMappingNode();
                metadata.Add("annotations", annotations);

                foreach (var annotation in deployment.Annotations)
                {
                    annotations.Add(annotation.Key, new YamlScalarNode(annotation.Value)
                    {
                        Style = ScalarStyle.SingleQuoted,
                    });
                }
            }

            labels = new YamlMappingNode();
            metadata.Add("labels", labels);
            foreach (var label in deployment.Labels)
            {
                labels.Add(label.Key, new YamlScalarNode(label.Value)
                {
                    Style = ScalarStyle.SingleQuoted,
                });
            }

            spec = new YamlMappingNode();
            template.Add("spec", spec);

            var containers = new YamlSequenceNode();

            spec.Add("containers", containers);

            var images = project.Outputs.OfType <DockerImageOutput>();

            foreach (var image in images)
            {
                var container = new YamlMappingNode();
                containers.Add(container);
                container.Add("name", project.Name);        // NOTE: to really support multiple images we'd need to generate unique names.
                container.Add("image", $"{image.ImageName}:{image.ImageTag}");
                container.Add("imagePullPolicy", "Always"); // helps avoid problems with development + weak versioning

                if (project.EnvironmentVariables.Count > 0 ||

                    // We generate ASPNETCORE_URLS if there are bindings for http
                    project.Bindings.Any(b => b.Protocol == "http" || b.Protocol is null) ||

                    // We generate environment variables for other services if there dependencies
                    (bindings is object && bindings.Bindings.Any()))
                {
                    var env = new YamlSequenceNode();
                    container.Add("env", env);

                    foreach (var kvp in project.EnvironmentVariables)
                    {
                        env.Add(new YamlMappingNode()
                        {
                            { "name", kvp.Name },
                            { "value", new YamlScalarNode(kvp.Value)
                              {
                                  Style = ScalarStyle.SingleQuoted,
                              } },
                        });
                    }

                    if (bindings is object)
                    {
                        foreach (var binding in bindings.Bindings.OfType <EnvironmentVariableInputBinding>())
                        {
                            env.Add(new YamlMappingNode()
                            {
                                { "name", binding.Name },
                                { "value", new YamlScalarNode(binding.Value)
                                  {
                                      Style = ScalarStyle.SingleQuoted,
                                  } },
                            });
                        }

                        foreach (var binding in bindings.Bindings.OfType <SecretInputBinding>())
                        {
                            //- name: SECRET_USERNAME
                            //  valueFrom:
                            //    secretKeyRef:
                            //      name: mysecret
                            //      key: username

                            if (binding is SecretConnectionStringInputBinding connectionStringBinding)
                            {
                                env.Add(new YamlMappingNode()
                                {
                                    { "name", connectionStringBinding.KeyName },
                                    { "valueFrom", new YamlMappingNode()
                                      {
                                          { "secretKeyRef", new YamlMappingNode()
                                            {
                                                { "name", new YamlScalarNode(binding.Name)
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                                { "key", new YamlScalarNode("connectionstring")
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                            } },
                                      } },
                                });
                            }
                            else if (binding is SecretUrlInputBinding urlBinding)
                            {
                                env.Add(new YamlMappingNode()
                                {
                                    { "name", $"{urlBinding.KeyNameBase}__PROTOCOL" },
                                    { "valueFrom", new YamlMappingNode()
                                      {
                                          { "secretKeyRef", new YamlMappingNode()
                                            {
                                                { "name", new YamlScalarNode(binding.Name)
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                                { "key", new YamlScalarNode("protocol")
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                            } },
                                      } },
                                });

                                env.Add(new YamlMappingNode()
                                {
                                    { "name", $"{urlBinding.KeyNameBase}__HOST" },
                                    { "valueFrom", new YamlMappingNode()
                                      {
                                          { "secretKeyRef", new YamlMappingNode()
                                            {
                                                { "name", new YamlScalarNode(binding.Name)
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                                { "key", new YamlScalarNode("host")
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                            } },
                                      } },
                                });

                                env.Add(new YamlMappingNode()
                                {
                                    { "name", $"{urlBinding.KeyNameBase}__PORT" },
                                    { "valueFrom", new YamlMappingNode()
                                      {
                                          { "secretKeyRef", new YamlMappingNode()
                                            {
                                                { "name", new YamlScalarNode(binding.Name)
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                                { "key", new YamlScalarNode("port")
                                                    {
                                                        Style = ScalarStyle.SingleQuoted
                                                    } },
                                            } },
                                      } },
                                });
                            }
                        }
                    }
                }

                if (project.Bindings.Count > 0)
                {
                    var ports = new YamlSequenceNode();
                    container.Add("ports", ports);

                    foreach (var binding in project.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 containerPort = new YamlMappingNode();
                            ports.Add(containerPort);
                            containerPort.Add("containerPort", binding.Port.Value.ToString());
                        }
                    }
                }
            }

            return(new KubernetesDeploymentOutput(project.Name, new YamlDocument(root)));
        }
Beispiel #3
0
        public static KubernetesDeploymentOutput CreateDeployment(
            OutputContext output,
            ApplicationBuilder application,
            ProjectServiceBuilder project,
            DeploymentManifestInfo deployment)
        {
            var bindings = project.Outputs.OfType <ComputedBindings>().FirstOrDefault();

            var root = new YamlMappingNode
            {
                { "kind", "Deployment" },
                { "apiVersion", "apps/v1" }
            };

            var metadata = new YamlMappingNode();

            root.Add("metadata", metadata);
            metadata.Add("name", project.Name);
            if (!string.IsNullOrEmpty(application.Namespace))
            {
                metadata.Add("namespace", application.Namespace);
            }

            if (deployment.Annotations.Count > 0)
            {
                var annotations = new YamlMappingNode();
                metadata.Add("annotations", annotations);

                foreach (var annotation in deployment.Annotations)
                {
                    annotations.Add(annotation.Key, new YamlScalarNode(annotation.Value)
                    {
                        Style = ScalarStyle.SingleQuoted,
                    });
                }
            }

            var labels = new YamlMappingNode();

            metadata.Add("labels", labels);
            foreach (var label in deployment.Labels)
            {
                labels.Add(label.Key, new YamlScalarNode(label.Value)
                {
                    Style = ScalarStyle.SingleQuoted,
                });
            }

            var spec = new YamlMappingNode();

            root.Add("spec", spec);

            spec.Add("replicas", project.Replicas.ToString());

            var selector = new YamlMappingNode();

            spec.Add("selector", selector);

            var matchLabels = new YamlMappingNode();

            selector.Add("matchLabels", matchLabels);

            // We need the name so we can use it with matchLabels.
            if (!deployment.Labels.TryGetValue("app.kubernetes.io/name", out var matchLabelsLabelValue))
            {
                throw new InvalidOperationException("The label 'app.kubernetes.io/name` is required.");
            }
            matchLabels.Add("app.kubernetes.io/name", matchLabelsLabelValue);

            var template = new YamlMappingNode();

            spec.Add("template", template);

            metadata = new YamlMappingNode();
            template.Add("metadata", metadata);

            if (deployment.Annotations.Count > 0)
            {
                var annotations = new YamlMappingNode();
                metadata.Add("annotations", annotations);

                foreach (var annotation in deployment.Annotations)
                {
                    annotations.Add(annotation.Key, new YamlScalarNode(annotation.Value)
                    {
                        Style = ScalarStyle.SingleQuoted,
                    });
                }
            }

            labels = new YamlMappingNode();
            metadata.Add("labels", labels);
            foreach (var label in deployment.Labels)
            {
                labels.Add(label.Key, new YamlScalarNode(label.Value)
                {
                    Style = ScalarStyle.SingleQuoted,
                });
            }

            spec = new YamlMappingNode();
            template.Add("spec", spec);

            if (project.Sidecars.Count > 0)
            {
                // Share process namespace when we have sidecars. So we can list other processes.
                // see: https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/#understanding-process-namespace-sharing
                spec.Add("shareProcessNamespace", new YamlScalarNode("true")
                {
                    Style = ScalarStyle.Plain
                });
            }

            if (project.RelocateDiagnosticsDomainSockets)
            {
                // Our diagnostics functionality uses $TMPDIR to locate other dotnet processes through
                // eventpipe. see: https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md#transport
                //
                // In order for diagnostics features to 'find' each other, we need to make $TMPDIR into
                // something shared.
                //
                // see: https://kubernetes.io/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/
                project.EnvironmentVariables.Add(new EnvironmentVariableBuilder("TMPDIR")
                {
                    Value = "/var/tye/diagnostics",
                });

                foreach (var sidecar in project.Sidecars)
                {
                    sidecar.EnvironmentVariables.Add(new EnvironmentVariableBuilder("TMPDIR")
                    {
                        Value = "/var/tye/diagnostics",
                    });
                }
            }

            var containers = new YamlSequenceNode();

            spec.Add("containers", containers);

            var images = project.Outputs.OfType <DockerImageOutput>();

            foreach (var image in images)
            {
                var container = new YamlMappingNode();
                containers.Add(container);
                container.Add("name", project.Name);        // NOTE: to really support multiple images we'd need to generate unique names.
                container.Add("image", $"{image.ImageName}:{image.ImageTag}");
                container.Add("imagePullPolicy", "Always"); // helps avoid problems with development + weak versioning


                var volumeMounts = new YamlSequenceNode();
                var localMounts  = new YamlSequenceNode();

                if (project.Volumes.Count > 0)
                {
                    container.Add("volumeMounts", volumeMounts);
                    spec.Add("volumes", localMounts);

                    foreach (var vol in project.Volumes)
                    {
                        var volumeMount = new YamlMappingNode();
                        volumeMounts.Add(volumeMount);
                        volumeMount.Add("name", vol.Name);
                        volumeMount.Add("mountPath", vol.Target);

                        var localMount = new YamlMappingNode();
                        var hostPath   = new YamlMappingNode();
                        var path       = new YamlMappingNode();

                        localMounts.Add(localMount);
                        localMount.Add("name", vol.Name);
                        localMount.Add("hostPath", hostPath);
                        hostPath.Add("path", vol.Source);
                    }
                }

                if (project.EnvironmentVariables.Count > 0 ||

                    // We generate ASPNETCORE_URLS if there are bindings for http
                    project.Bindings.Any(b => b.Protocol == "http" || b.Protocol is null) ||

                    // We generate environment variables for other services if there dependencies
                    bindings?.Bindings.Count > 0)
                {
                    var env = new YamlSequenceNode();
                    container.Add("env", env);

                    foreach (var kvp in project.EnvironmentVariables)
                    {
                        env.Add(new YamlMappingNode()
                        {
                            { "name", kvp.Name },
                            { "value", new YamlScalarNode(kvp.Value)
                              {
                                  Style = ScalarStyle.SingleQuoted,
                              } },
                        });
                    }

                    if (bindings != null)
                    {
                        AddEnvironmentVariablesForComputedBindings(env, bindings);
                    }

                    if (project.RelocateDiagnosticsDomainSockets)
                    {
                        // volumeMounts:
                        // - name: shared-data
                        //   mountPath: /usr/share/nginx/html
                        if (!container.Children.ContainsKey("volumeMounts"))
                        {
                            container.Add("volumeMounts", volumeMounts);
                        }

                        var volumeMount = new YamlMappingNode();
                        volumeMounts.Add(volumeMount);
                        volumeMount.Add("name", "tye-diagnostics");
                        volumeMount.Add("mountPath", "/var/tye/diagnostics");
                    }
                }

                if (project.Bindings.Count > 0)
                {
                    var ports = new YamlSequenceNode();
                    container.Add("ports", ports);

                    foreach (var binding in project.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)
                        {
                            continue;
                        }

                        var containerPort = new YamlMappingNode();
                        ports.Add(containerPort);
                        containerPort.Add("containerPort", (binding.ContainerPort ?? binding.Port.Value).ToString());
                    }
                }

                if (project.Liveness != null)
                {
                    AddProbe(project, container, project.Liveness !, "livenessProbe");
                }

                if (project.Readiness != null)
                {
                    AddProbe(project, container, project.Readiness !, "readinessProbe");
                }
            }

            foreach (var sidecar in project.Sidecars)
            {
                var container = new YamlMappingNode();
                containers.Add(container);
                container.Add("name", sidecar.Name);        // NOTE: to really support multiple images we'd need to generate unique names.
                container.Add("image", $"{sidecar.ImageName}:{sidecar.ImageTag}");
                container.Add("imagePullPolicy", "Always"); // helps avoid problems with development + weak versioning

                if (sidecar.Args.Count > 0)
                {
                    var args = new YamlSequenceNode();
                    container.Add("args", args);

                    foreach (var arg in sidecar.Args)
                    {
                        args.Add(new YamlScalarNode(arg)
                        {
                            Style = ScalarStyle.SingleQuoted,
                        });
                    }
                }

                var sidecarBindings = sidecar.Outputs.OfType <ComputedBindings>().FirstOrDefault();
                if (sidecar.EnvironmentVariables.Count > 0 || sidecarBindings?.Bindings.Count > 0)
                {
                    var env = new YamlSequenceNode();
                    container.Add("env", env);

                    foreach (var kvp in sidecar.EnvironmentVariables)
                    {
                        env.Add(new YamlMappingNode()
                        {
                            { "name", kvp.Name },
                            { "value", new YamlScalarNode(kvp.Value)
                              {
                                  Style = ScalarStyle.SingleQuoted,
                              } },
                        });
                    }

                    if (sidecarBindings != null)
                    {
                        AddEnvironmentVariablesForComputedBindings(env, sidecarBindings);
                    }
                }

                if (!project.RelocateDiagnosticsDomainSockets)
                {
                    continue;
                }

                // volumeMounts:
                // - name: shared-data
                //   mountPath: /usr/share/nginx/html
                var volumeMounts = new YamlSequenceNode();
                container.Add("volumeMounts", volumeMounts);

                var volumeMount = new YamlMappingNode();
                volumeMounts.Add(volumeMount);
                volumeMount.Add("name", "tye-diagnostics");
                volumeMount.Add("mountPath", "/var/tye/diagnostics");
            }

            if (!project.RelocateDiagnosticsDomainSockets)
            {
                return(new KubernetesDeploymentOutput(project.Name, new YamlDocument(root)));
            }
            // volumes:
            // - name: shared-data
            //   emptyDir: {}
            var volumes = new YamlSequenceNode();

            spec.Add("volumes", volumes);

            var volume = new YamlMappingNode();

            volumes.Add(volume);
            volume.Add("name", "tye-diagnostics");
            volume.Add("emptyDir", new YamlMappingNode());

            return(new KubernetesDeploymentOutput(project.Name, new YamlDocument(root)));
        }