From f1fba0a14c5bf515f3a577c4670fdba437b5bec2 Mon Sep 17 00:00:00 2001
From: Alex Clemmer <clemmer.alexander@gmail.com>
Date: Mon, 25 Sep 2017 22:14:43 -0700
Subject: [PATCH] Add combination deployment/service system prototype

---
 prototype/prototype_test.go   |   4 +
 prototype/systemPrototypes.go | 169 ++++++++++++++++++++++++++++++----
 2 files changed, 156 insertions(+), 17 deletions(-)

diff --git a/prototype/prototype_test.go b/prototype/prototype_test.go
index 5f78632b..ff9e9df3 100644
--- a/prototype/prototype_test.go
+++ b/prototype/prototype_test.go
@@ -113,6 +113,7 @@ func TestSearch(t *testing.T) {
 		"io.ksonnet.pkg.single-port-service",
 		"io.ksonnet.pkg.namespace",
 		"io.ksonnet.pkg.configMap",
+		"io.ksonnet.pkg.deployment-exposed-with-service",
 		"io.ksonnet.pkg.single-port-deployment",
 	})
 	assertSearch(t, idx, Prefix, "foo", []string{})
@@ -120,6 +121,7 @@ func TestSearch(t *testing.T) {
 	// Suffix searches.
 	assertSearch(t, idx, Suffix, "service", []string{
 		"io.ksonnet.pkg.single-port-service",
+		"io.ksonnet.pkg.deployment-exposed-with-service",
 		"io.some-vendor.pkg.simple-service",
 	})
 	assertSearch(t, idx, Suffix, "simple", []string{})
@@ -129,6 +131,7 @@ func TestSearch(t *testing.T) {
 	// Substring searches.
 	assertSearch(t, idx, Substring, "service", []string{
 		"io.ksonnet.pkg.single-port-service",
+		"io.ksonnet.pkg.deployment-exposed-with-service",
 		"io.some-vendor.pkg.simple-service",
 	})
 	assertSearch(t, idx, Substring, "simple", []string{
@@ -136,6 +139,7 @@ func TestSearch(t *testing.T) {
 		"io.some-vendor.pkg.simple-service",
 	})
 	assertSearch(t, idx, Substring, "io.ksonnet", []string{
+		"io.ksonnet.pkg.deployment-exposed-with-service",
 		"io.ksonnet.pkg.single-port-service",
 		"io.ksonnet.pkg.single-port-deployment",
 		"io.ksonnet.pkg.configMap",
diff --git a/prototype/systemPrototypes.go b/prototype/systemPrototypes.go
index cbe40c85..5fe490b8 100644
--- a/prototype/systemPrototypes.go
+++ b/prototype/systemPrototypes.go
@@ -47,9 +47,10 @@ namespace.`,
 		Params: ParamSchemas{
 			RequiredParam("name", "serviceName", "Name of the service", String),
 			RequiredParam("targetLabelSelector", "selector", `Label for the service to target (e.g., "{app: 'MyApp'}").`, Object),
-			RequiredParam("servicePort", "port", "Port for the service to expose.", NumberOrString),
-			RequiredParam("targetPort", "port", "Port for the service target.", NumberOrString),
+			OptionalParam("servicePort", "port", "Port for the service to expose.", "80", NumberOrString),
+			OptionalParam("targetPort", "port", "Port for the service target.", "80", NumberOrString),
 			OptionalParam("protocol", "protocol", "Protocol to use (either TCP or UDP).", "TCP", String),
+			OptionalParam("type", "serviceType", "Type of service to expose", "ClusterIP", String),
 		},
 		Template: SnippetSchema{
 			Description: `A service that exposes 'servicePort', and directs traffic
@@ -60,17 +61,18 @@ will typically look something like:
   ksonnet prototype use service --targetLabelSelector "{app: 'nginx'}" [...]`,
 			ShortDescription: `Service that exposes a single port`,
 			YAMLBody: []string{
-				"kind: Service",
-				"apiVersion: v1",
-				"metadata:",
-				"  name: ${name}",
-				"spec:",
-				"  selector:",
-				"    ${targetLabelSelector}",
-				"  ports:",
-				"  - protocol: ${protocol}",
-				"    port: ${servicePort}",
-				"    targetPort: ${targetPort}",
+				`kind: Service`,
+				`apiVersion: v1`,
+				`metadata:`,
+				`  name: ${name}`,
+				`spec:`,
+				`  selector:`,
+				`    ${targetLabelSelector}`,
+				`  type: ${type}`,
+				`  ports:`,
+				`  - protocol: ${protocol}`,
+				`    port: ${servicePort}`,
+				`    targetPort: ${targetPort}`,
 			},
 			JSONBody: []string{
 				`{`,
@@ -80,9 +82,9 @@ will typically look something like:
 				`    "name": ${name}`,
 				`  },`,
 				`  "spec": {`,
-				`    "selector": {`,
-				`      ${targetLabelSelector}`,
-				`    },`,
+				`    "selector":`,
+				`      ${targetLabelSelector},`,
+				`    "type": ${type},`,
 				`    "ports": [`,
 				`      {`,
 				`        "protocol": ${protocol},`,
@@ -101,7 +103,140 @@ will typically look something like:
 				`service.new(`,
 				`  ${name},`,
 				`  ${targetLabelSelector},`,
-				`  port.new(${servicePort}, ${targetPort}))`,
+				`  port.new(${servicePort}, ${targetPort})) +`,
+				`service.mixin.spec.type(${type})`,
+			},
+		},
+	},
+	&SpecificationSchema{
+		APIVersion: "0.1",
+		Name:       "io.ksonnet.pkg.deployment-exposed-with-service",
+		Params: ParamSchemas{
+			RequiredParam("name", "name", "Name of the service and deployment", String),
+			RequiredParam("image", "containerImage", "Container image to deploy", String),
+			OptionalParam("servicePort", "port", "Port for the service to expose.", "80", NumberOrString),
+			OptionalParam("containerPort", "port", "Container port for service to target.", "80", NumberOrString),
+			OptionalParam("replicas", "replicas", "Number of replicas", "1", Number),
+			OptionalParam("type", "serviceType", "Type of service to expose", "ClusterIP", String),
+		},
+		Template: SnippetSchema{
+			Description: `A service that exposes 'servicePort', and directs traffic
+to 'targetLabelSelector', at 'targetPort'.`,
+			ShortDescription: `A deployment exposed with a service`,
+			YAMLBody: []string{
+				`apiVersion: v1`,
+				`items:`,
+				`  - apiVersion: v1`,
+				`    kind: Service`,
+				`    metadata:`,
+				`      name: ${name}`,
+				`    spec:`,
+				`      ports:`,
+				`        - port: ${servicePort}`,
+				`          targetPort: ${containerPort}`,
+				`      selector:`,
+				`        app: ${name}`,
+				`      type: ${type}`,
+				`  - apiVersion: apps/v1beta1`,
+				`    kind: Deployment`,
+				`    metadata:`,
+				`      name: ${name}`,
+				`    spec:`,
+				`      replicas: ${replicas}`,
+				`      template:`,
+				`        metadata:`,
+				`          labels:`,
+				`            app: ${name}`,
+				`        spec:`,
+				`          containers:`,
+				`            - image: ${name}`,
+				`              name: ${image}`,
+				`              ports:`,
+				`                - containerPort: ${containerPort}`,
+				`kind: List`,
+			},
+			JSONBody: []string{
+				`{`,
+				`  "apiVersion": "v1",`,
+				`  "items": [`,
+				`    {`,
+				`      "apiVersion": "v1",`,
+				`      "kind": "Service",`,
+				`      "metadata": {`,
+				`        "name": ${name}`,
+				`      },`,
+				`      "spec": {`,
+				`        "ports": [`,
+				`          {`,
+				`            "port": ${servicePort},`,
+				`            "targetPort": ${containerPort}`,
+				`          }`,
+				`        ],`,
+				`        "selector": {`,
+				`          "app": ${name}`,
+				`        },`,
+				`        "type": ${type}`,
+				`      }`,
+				`    },`,
+				`    {`,
+				`      "apiVersion": "apps/v1beta1",`,
+				`      "kind": "Deployment",`,
+				`      "metadata": {`,
+				`        "name": ${name}`,
+				`      },`,
+				`      "spec": {`,
+				`        "replicas": ${replicas},`,
+				`        "template": {`,
+				`          "metadata": {`,
+				`            "labels": {`,
+				`              "app": ${name}`,
+				`            }`,
+				`          },`,
+				`          "spec": {`,
+				`            "containers": [`,
+				`              {`,
+				`                "image": ${name},`,
+				`                "name": ${image},`,
+				`                "ports": [`,
+				`                  {`,
+				`                    "containerPort": ${containerPort}`,
+				`                  }`,
+				`                ]`,
+				`              }`,
+				`            ]`,
+				`          }`,
+				`        }`,
+				`      }`,
+				`    }`,
+				`  ],`,
+				`  "kind": "List"`,
+				`}`,
+			},
+			JsonnetBody: []string{
+				`local k = import "k.libsonnet";`,
+				`local deployment = k.apps.v1beta1.deployment;`,
+				`local container = k.apps.v1beta1.deployment.mixin.spec.template.spec.containersType;`,
+				`local containerPort = container.portsType;`,
+				`local service = k.core.v1.service;`,
+				`local servicePort = k.core.v1.service.mixin.spec.portsType;`,
+				``,
+				`local targetPort = ${containerPort};`,
+				`local labels = {app: ${name}};`,
+				``,
+				`local appService = service.new(`,
+				`  ${name},`,
+				`  labels,`,
+				`  servicePort.new(${servicePort}, targetPort)) +`,
+				`service.mixin.spec.type(${type});`,
+				``,
+				`local appDeployment = deployment.new(`,
+				`  ${name},`,
+				`  ${replicas},`,
+				`  container.new(${name}, ${image}) +`,
+				`    container.ports(containerPort.new(targetPort)),`,
+				`  labels);`,
+				``,
+				`k.core.v1.list.new([appService, appDeployment])`,
 			},
 		},
 	},
-- 
GitLab