├── all.jsonnet ├── env.json ├── README.md ├── test └── functional_test.jsonnet ├── apps ├── scheduler.svc.json ├── controller-manager.svc.json ├── scheduler.deployment.json ├── kubelet.ds.json └── controller-manager.deployment.json ├── generate_config ├── ks-deployment.jsonnet ├── run_tests ├── create-secret ├── kubelet-ds.jsonnet ├── kcm-deployment.jsonnet └── lib ├── functional.jsonnet └── kube.jsonnet /all.jsonnet: -------------------------------------------------------------------------------- 1 | (import "kcm-deployment.jsonnet") + 2 | (import "ks-deployment.jsonnet") + 3 | (import "kubelet-ds.jsonnet") 4 | -------------------------------------------------------------------------------- /env.json: -------------------------------------------------------------------------------- 1 | { 2 | "kubeletTag": "green", 3 | "kubeProxyTag": "green", 4 | "kubeControllerManagerTag": "blue", 5 | "kubeSchedulerTag": "green" 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k8s-on-k8s 2 | 3 | https://goo.gl/jQCxLL 4 | 5 | Configs for running k8s-on-k8s. 6 | 7 | The configs in apps/ are autogenerated. 8 | 9 | Try it by running: 10 | ```console 11 | $ ./generate_config 12 | ``` 13 | Or just: 14 | ```console 15 | $ jsonnet --multi apps/ all.jsonnet 16 | ``` 17 | -------------------------------------------------------------------------------- /test/functional_test.jsonnet: -------------------------------------------------------------------------------- 1 | local functional = import "lib/functional.jsonnet"; 2 | 3 | local blah = { 4 | foo: { 5 | bar: "blah", 6 | }, 7 | }; 8 | 9 | local out = functional.bind([ 10 | functional.replaceField(".foo.bar", { bing: "bang" }), 11 | functional.mergePatchField(".foo.bar", { baz: "dingus" }), 12 | ])(blah); 13 | 14 | std.assertEqual(out, { 15 | foo: { 16 | bar: { 17 | baz: "dingus", 18 | bing: "bang", 19 | }, 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /apps/scheduler.svc.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "labels": { 6 | "component": "scheduler", 7 | "tier": "control-plane" 8 | }, 9 | "name": "kube-scheduler", 10 | "namespace": "kube-system" 11 | }, 12 | "spec": { 13 | "ports": [ 14 | { 15 | "port": 10251, 16 | "targetPort": 10251 17 | } 18 | ], 19 | "selector": { 20 | "component": "scheduler", 21 | "tier": "control-plane" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/controller-manager.svc.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "labels": { 6 | "component": "controller-manager", 7 | "tier": "control-plane" 8 | }, 9 | "name": "kube-controller-manager", 10 | "namespace": "kube-system" 11 | }, 12 | "spec": { 13 | "ports": [ 14 | { 15 | "port": 10252, 16 | "targetPort": 10252 17 | } 18 | ], 19 | "selector": { 20 | "component": "controller-manager", 21 | "tier": "control-plane" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /generate_config: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -o nounset 4 | set -o errexit 5 | set -o pipefail 6 | 7 | red="$(tput setaf 1)" 8 | green="$(tput setaf 2)" 9 | reset="$(tput sgr0)" 10 | 11 | declare -r OUT_DIR="apps/" 12 | 13 | templates=( 14 | "ks-deployment.jsonnet" 15 | "kcm-deployment.jsonnet" 16 | "kubelet-ds.jsonnet" 17 | ) 18 | 19 | mkdir -p "${OUT_DIR}" 20 | 21 | for template in ${templates[@]}; do 22 | cmd=( 23 | "jsonnet" 24 | "--multi" 25 | "${OUT_DIR}" 26 | "${template}" 27 | ) 28 | echo "generating: ${template}" 29 | "${cmd[@]}" \ 30 | | sed -e "s/^/ ${green}/" \ 31 | | sed -e "s/$/${reset}/" 32 | done 33 | 34 | -------------------------------------------------------------------------------- /ks-deployment.jsonnet: -------------------------------------------------------------------------------- 1 | local k8s = import "lib/kube.jsonnet"; 2 | local env = import "env.json"; 3 | 4 | local config = k8s.DefaultConfig({ 5 | name: "kube-scheduler", 6 | namespace: "kube-system", 7 | tier: "control-plane", 8 | labels: { 9 | component: "scheduler", 10 | }, 11 | pod: { 12 | command: [ 13 | "/usr/local/bin/kube-scheduler", 14 | "--address=0.0.0.0", 15 | "--v=2", 16 | "--leader-elect", 17 | ], 18 | image: "kube-scheduler", 19 | tag: env.kubeSchedulerTag, 20 | port: 10251, 21 | }, 22 | }); 23 | 24 | { 25 | "scheduler.svc.json": k8s.Service(config), 26 | "scheduler.deployment.json": k8s.Deployment(config), 27 | } 28 | -------------------------------------------------------------------------------- /run_tests: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -o nounset 4 | set -o errexit 5 | set -o pipefail 6 | 7 | red="$(tput setaf 1)" 8 | green="$(tput setaf 2)" 9 | reset="$(tput sgr0)" 10 | 11 | TEST_DIR="test/" 12 | 13 | failed="false" 14 | 15 | for test_file in $(find ${TEST_DIR} -type f); do 16 | echo "testing ${test_file}" 17 | cmd=("jsonnet" "--jpath" "." "${test_file}") 18 | output=$("${cmd[@]}" 2>&1) && errcode=0 || errcode="$?" 19 | if [[ "${output}" == "true" && "${errcode}" == 0 ]]; then 20 | echo "${green}PASS${reset}" 21 | else 22 | echo "${red}FAIL${reset}" 23 | echo " code: ${errcode}" 24 | echo " out: ${output}" 25 | failed="true" 26 | fi 27 | echo 28 | done 29 | 30 | if [[ "$failed" == "true" ]]; then 31 | echo "some tests failed" 32 | exit 1 33 | fi 34 | 35 | echo "all tests passed" 36 | exit 0 37 | -------------------------------------------------------------------------------- /create-secret: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -o nounset 4 | set -o errexit 5 | set -o pipefail 6 | 7 | if [[ -z "${K8S_MASTER:-}" ]]; then 8 | echo "Please set K8S_MASTER before running this" > /dev/stderr 9 | exit 1 10 | fi 11 | 12 | export ROOT_CA_FILE="$(ssh ${K8S_MASTER} 'sudo cat /srv/kubernetes/ca.crt')" 13 | export SERVER_KEY="$(ssh ${K8S_MASTER} 'sudo cat /srv/kubernetes/server.key')" 14 | 15 | cat << EOF | \ 16 | jsonnet --env ROOT_CA_FILE --env SERVER_KEY - \ 17 | | tee /dev/stderr \ 18 | | kubectl apply -f - 19 | 20 | local rootCAFile = std.base64(std.extVar("ROOT_CA_FILE")); 21 | local serverKey = std.base64(std.extVar("SERVER_KEY")); 22 | 23 | { 24 | "apiVersion": "v1", 25 | "kind": "Secret", 26 | "metadata": { 27 | "name": "cm-secrets", 28 | "namespace": "kube-system", 29 | }, 30 | "type": "Opaque", 31 | "data": { 32 | "root-ca-file": rootCAFile, 33 | "server-key": serverKey, 34 | } 35 | } 36 | EOF 37 | -------------------------------------------------------------------------------- /kubelet-ds.jsonnet: -------------------------------------------------------------------------------- 1 | local k8s = import "lib/kube.jsonnet"; 2 | local env = import "env.json"; 3 | 4 | local config = k8s.DefaultConfig({ 5 | name: "kubelet-%s" % env.kubeletTag, 6 | namespace: "kube-system", 7 | tier: "node", 8 | labels: { 9 | component: "kubelet", 10 | }, 11 | pod: { 12 | command: [ 13 | "nsenter", 14 | "--target=1", 15 | "--mount", 16 | "--wd=.", 17 | "--", 18 | "./kubelet", 19 | "--api-servers=https://k-1-master", 20 | "--enable-debugging-handlers=true", 21 | "--cloud-provider=gce", 22 | "--config=/etc/kubernetes/manifests", 23 | "--allow-privileged=True", 24 | "--v=2", 25 | "--cluster-dns=10.0.0.10", 26 | "--cluster-domain=cluster.local", 27 | "--cgroup-root=/", 28 | "--system-container=/system", 29 | ], 30 | image: "kubelet", 31 | tag: env.kubeletTag, 32 | privileged: true, 33 | httpLiveness: false, 34 | hostPID: true, 35 | hostNetwork: true, 36 | port: 10250, 37 | }, 38 | }); 39 | 40 | { 41 | "kubelet.ds.json": k8s.DaemonSet(config), 42 | } 43 | -------------------------------------------------------------------------------- /kcm-deployment.jsonnet: -------------------------------------------------------------------------------- 1 | local k8s = import "lib/kube.jsonnet"; 2 | local env = import "env.json"; 3 | 4 | local config = k8s.DefaultConfig({ 5 | name: "kube-controller-manager", 6 | namespace: "kube-system", 7 | tier: "control-plane", 8 | labels: { 9 | component: "controller-manager", 10 | }, 11 | pod: { 12 | command: [ 13 | "/usr/local/bin/kube-controller-manager", 14 | "--address=0.0.0.0", 15 | "--cluster-name=k-1", 16 | "--cluster-cidr=10.244.0.0/16", 17 | "--allocate-node-cidrs=true", 18 | "--cloud-provider=gce", 19 | "--service-account-private-key-file=/srv/kubernetes/server-key", 20 | "--root-ca-file=/srv/kubernetes/root-ca-file", 21 | "--leader-elect", 22 | "--v=2", 23 | ], 24 | image: "kube-controller-manager", 25 | tag: env.kubeControllerManagerTag, 26 | port: 10252, 27 | hostVolumes: [ 28 | "/etc/ssl", 29 | "/usr/share/ssl", 30 | "/var/ssl", 31 | "/usr/ssl", 32 | "/usr/lib/ssl", 33 | "/usr/local/openssl", 34 | "/etc/openssl", 35 | "/etc/pki/tls", 36 | ], 37 | secrets: [ 38 | "cm-secrets", 39 | ], 40 | }, 41 | }); 42 | 43 | { 44 | "controller-manager.svc.json": k8s.Service(config), 45 | "controller-manager.deployment.json": k8s.Deployment(config), 46 | } 47 | -------------------------------------------------------------------------------- /lib/functional.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | identity(obj):: 3 | obj, 4 | 5 | constant(k):: 6 | function(obj) 7 | k, 8 | 9 | bind(arr):: 10 | std.foldl(function(func1, func2) 11 | function(obj) 12 | func2(func1(obj)), 13 | arr, 14 | $.identity), 15 | 16 | nullable(func):: 17 | function(obj) 18 | if obj == null then 19 | null 20 | else 21 | func(obj), 22 | 23 | # useful for traversals 24 | jqSegments(path):: 25 | local segments = std.split(path, "."); 26 | local len = std.length(segments); 27 | if len == 0 then 28 | error "must pass a real path" 29 | else if segments[0] != "" then 30 | error "jq path must start with a '.'" 31 | else 32 | std.makeArray(len - 1, function(i) segments[i + 1]), 33 | 34 | # doesn't hand objects with '.' in their keys yet. 35 | visitField(segments, func):: 36 | function(obj) 37 | local len = std.length(segments); 38 | if len > 0 then 39 | local shorten = std.makeArray(len - 1, function(i) segments[i + 1]); 40 | { 41 | [k]: 42 | if k == segments[0] then 43 | $.visitField(shorten, func)(obj[k]) tailstrict 44 | else 45 | obj[k] 46 | for k in std.objectFields(obj) 47 | } 48 | else 49 | func(obj), 50 | 51 | replaceField(path, obj):: 52 | $.visitField($.jqSegments(path), $.constant(obj)), 53 | 54 | mergePatchField(path, obj):: 55 | $.visitField($.jqSegments(path), function(target) std.mergePatch(target, obj)), 56 | } 57 | -------------------------------------------------------------------------------- /apps/scheduler.deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "labels": { 6 | "component": "scheduler", 7 | "tier": "control-plane" 8 | }, 9 | "name": "kube-scheduler", 10 | "namespace": "kube-system" 11 | }, 12 | "spec": { 13 | "replicas": 3, 14 | "selector": { 15 | "matchLabels": { 16 | "component": "scheduler", 17 | "tier": "control-plane" 18 | } 19 | }, 20 | "template": { 21 | "metadata": { 22 | "labels": { 23 | "component": "scheduler", 24 | "tier": "control-plane", 25 | "version": "green" 26 | } 27 | }, 28 | "spec": { 29 | "containers": [ 30 | { 31 | "command": [ 32 | "/usr/local/bin/kube-scheduler", 33 | "--address=0.0.0.0", 34 | "--v=2", 35 | "--leader-elect" 36 | ], 37 | "image": "gcr.io/mikedanese-k8s/kube-scheduler:green", 38 | "livenessProbe": { 39 | "httpGet": { 40 | "path": "/healthz", 41 | "port": 10251 42 | }, 43 | "initialDelaySeconds": 15 44 | }, 45 | "name": "kube-scheduler", 46 | "readinessProbe": { 47 | "httpGet": { 48 | "path": "/healthz", 49 | "port": 10251 50 | } 51 | }, 52 | "resources": { 53 | "requests": { 54 | "cpu": "0.1" 55 | } 56 | }, 57 | "securityContext": { 58 | "privileged": false 59 | }, 60 | "volumeMounts": [ ] 61 | } 62 | ], 63 | "hostNetwork": false, 64 | "hostPID": false, 65 | "volumes": [ ] 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /apps/kubelet.ds.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "DaemonSet", 4 | "metadata": { 5 | "labels": { 6 | "component": "kubelet", 7 | "tier": "node" 8 | }, 9 | "name": "kubelet-green", 10 | "namespace": "kube-system" 11 | }, 12 | "spec": { 13 | "template": { 14 | "metadata": { 15 | "labels": { 16 | "component": "kubelet", 17 | "tier": "node", 18 | "version": "green" 19 | } 20 | }, 21 | "spec": { 22 | "containers": [ 23 | { 24 | "command": [ 25 | "nsenter", 26 | "--target=1", 27 | "--mount", 28 | "--wd=.", 29 | "--", 30 | "./kubelet", 31 | "--api-servers=https://k-1-master", 32 | "--enable-debugging-handlers=true", 33 | "--cloud-provider=gce", 34 | "--config=/etc/kubernetes/manifests", 35 | "--allow-privileged=True", 36 | "--v=2", 37 | "--cluster-dns=10.0.0.10", 38 | "--cluster-domain=cluster.local", 39 | "--cgroup-root=/", 40 | "--system-container=/system" 41 | ], 42 | "image": "gcr.io/mikedanese-k8s/kubelet:green", 43 | "livenessProbe": null, 44 | "name": "kubelet-green", 45 | "readinessProbe": null, 46 | "resources": { 47 | "requests": { 48 | "cpu": "0.1" 49 | } 50 | }, 51 | "securityContext": { 52 | "privileged": true 53 | }, 54 | "volumeMounts": [ ] 55 | } 56 | ], 57 | "hostNetwork": true, 58 | "hostPID": true, 59 | "nodeSelector": { 60 | "minion": "true" 61 | }, 62 | "volumes": [ ] 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /apps/controller-manager.deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "extensions/v1beta1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "labels": { 6 | "component": "controller-manager", 7 | "tier": "control-plane" 8 | }, 9 | "name": "kube-controller-manager", 10 | "namespace": "kube-system" 11 | }, 12 | "spec": { 13 | "replicas": 3, 14 | "selector": { 15 | "matchLabels": { 16 | "component": "controller-manager", 17 | "tier": "control-plane" 18 | } 19 | }, 20 | "template": { 21 | "metadata": { 22 | "labels": { 23 | "component": "controller-manager", 24 | "tier": "control-plane", 25 | "version": "blue" 26 | } 27 | }, 28 | "spec": { 29 | "containers": [ 30 | { 31 | "command": [ 32 | "/usr/local/bin/kube-controller-manager", 33 | "--address=0.0.0.0", 34 | "--cluster-name=k-1", 35 | "--cluster-cidr=10.244.0.0/16", 36 | "--allocate-node-cidrs=true", 37 | "--cloud-provider=gce", 38 | "--service-account-private-key-file=/srv/kubernetes/server-key", 39 | "--root-ca-file=/srv/kubernetes/root-ca-file", 40 | "--leader-elect", 41 | "--v=2" 42 | ], 43 | "image": "gcr.io/mikedanese-k8s/kube-controller-manager:blue", 44 | "livenessProbe": { 45 | "httpGet": { 46 | "path": "/healthz", 47 | "port": 10252 48 | }, 49 | "initialDelaySeconds": 15 50 | }, 51 | "name": "kube-controller-manager", 52 | "readinessProbe": { 53 | "httpGet": { 54 | "path": "/healthz", 55 | "port": 10252 56 | } 57 | }, 58 | "resources": { 59 | "requests": { 60 | "cpu": "0.1" 61 | } 62 | }, 63 | "securityContext": { 64 | "privileged": false 65 | }, 66 | "volumeMounts": [ 67 | { 68 | "mountPath": "/etc/ssl", 69 | "name": "etcssl", 70 | "readOnly": true 71 | }, 72 | { 73 | "mountPath": "/usr/share/ssl", 74 | "name": "usrsharessl", 75 | "readOnly": true 76 | }, 77 | { 78 | "mountPath": "/var/ssl", 79 | "name": "varssl", 80 | "readOnly": true 81 | }, 82 | { 83 | "mountPath": "/usr/ssl", 84 | "name": "usrssl", 85 | "readOnly": true 86 | }, 87 | { 88 | "mountPath": "/usr/lib/ssl", 89 | "name": "usrlibssl", 90 | "readOnly": true 91 | }, 92 | { 93 | "mountPath": "/usr/local/openssl", 94 | "name": "usrlocalopenssl", 95 | "readOnly": true 96 | }, 97 | { 98 | "mountPath": "/etc/openssl", 99 | "name": "etcopenssl", 100 | "readOnly": true 101 | }, 102 | { 103 | "mountPath": "/etc/pki/tls", 104 | "name": "etcpkitls", 105 | "readOnly": true 106 | }, 107 | { 108 | "mountPath": "/srv/kubernetes", 109 | "name": "cm-secrets", 110 | "readOnly": true 111 | } 112 | ] 113 | } 114 | ], 115 | "hostNetwork": false, 116 | "hostPID": false, 117 | "volumes": [ 118 | { 119 | "hostPath": { 120 | "path": "/etc/ssl" 121 | }, 122 | "name": "etcssl" 123 | }, 124 | { 125 | "hostPath": { 126 | "path": "/usr/share/ssl" 127 | }, 128 | "name": "usrsharessl" 129 | }, 130 | { 131 | "hostPath": { 132 | "path": "/var/ssl" 133 | }, 134 | "name": "varssl" 135 | }, 136 | { 137 | "hostPath": { 138 | "path": "/usr/ssl" 139 | }, 140 | "name": "usrssl" 141 | }, 142 | { 143 | "hostPath": { 144 | "path": "/usr/lib/ssl" 145 | }, 146 | "name": "usrlibssl" 147 | }, 148 | { 149 | "hostPath": { 150 | "path": "/usr/local/openssl" 151 | }, 152 | "name": "usrlocalopenssl" 153 | }, 154 | { 155 | "hostPath": { 156 | "path": "/etc/openssl" 157 | }, 158 | "name": "etcopenssl" 159 | }, 160 | { 161 | "hostPath": { 162 | "path": "/etc/pki/tls" 163 | }, 164 | "name": "etcpkitls" 165 | }, 166 | { 167 | "name": "cm-secrets", 168 | "secret": { 169 | "secretName": "cm-secrets" 170 | } 171 | } 172 | ] 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lib/kube.jsonnet: -------------------------------------------------------------------------------- 1 | /* 2 | This is somewhat complicated mostly because I'm testing out the capabilities 3 | of jsonnet. It's not really neccessary to understand this. 4 | */ 5 | { 6 | local functional = import "lib/functional.jsonnet", 7 | local kube = self, 8 | 9 | v1:: { 10 | local v1 = self, 11 | 12 | ApiVersion:: { apiVersion: "v1" }, 13 | 14 | List(items):: v1.ApiVersion { 15 | kind: "List", 16 | items: items, 17 | }, 18 | }, 19 | 20 | extensions:: { 21 | v1beta1:: { 22 | ApiVersion:: { apiVersion: "extensions/v1beta1" }, 23 | }, 24 | 25 | DaemonSet:: kube.extensions.v1beta1.ApiVersion { 26 | kind: "DaemonSet", 27 | }, 28 | 29 | Deployment:: kube.extensions.v1beta1.ApiVersion { 30 | kind: "Deployment", 31 | }, 32 | }, 33 | 34 | VolumeMounts(tab):: 35 | [{ name: k, mountPath: tab[k], readonly: false } for k in std.objectFields(tab)], 36 | 37 | HostVolumes(tab):: 38 | [{ name: k, hostPath: { path: tab[k] } } for k in std.objectFields(tab)], 39 | 40 | addLabels(labels):: 41 | function(obj) 42 | std.mergePatch(obj, { metadata: { labels: labels } }), 43 | 44 | PodSpec(config):: 45 | local pod = config.pod; 46 | { 47 | hostNetwork: pod.hostNetwork, 48 | hostPID: pod.hostPID, 49 | containers: [{ 50 | name: config.name, 51 | command: pod.command, 52 | image: "%s/%s:%s" % [pod.repository, pod.image, pod.tag], 53 | securityContext: { 54 | privileged: pod.privileged, 55 | }, 56 | livenessProbe: 57 | if pod.httpLiveness then 58 | { 59 | httpGet: { 60 | path: "/healthz", 61 | port: pod.port, 62 | }, 63 | initialDelaySeconds: 15, 64 | } 65 | else 66 | null, 67 | readinessProbe: 68 | if pod.httpLiveness then 69 | { 70 | httpGet: { 71 | path: "/healthz", 72 | port: pod.port, 73 | }, 74 | } 75 | else 76 | null, 77 | resources: { 78 | requests: { 79 | cpu: pod.resourceRequests.cpu, 80 | }, 81 | }, 82 | volumeMounts: [{ 83 | name: std.join("", std.split(path, "/")), 84 | mountPath: path, 85 | readOnly: true, 86 | } for path in pod.hostVolumes] + 87 | [{ 88 | name: name, 89 | mountPath: "/srv/kubernetes", 90 | readOnly: true, 91 | } for name in pod.secrets], 92 | }], 93 | volumes: [{ 94 | name: std.join("", std.split(path, "/")), 95 | hostPath: { path: path }, 96 | } for path in pod.hostVolumes] + 97 | [{ 98 | name: name, 99 | secret: { secretName: name }, 100 | } for name in pod.secrets], 101 | }, 102 | 103 | 104 | PodController(type, config):: 105 | local template = 106 | if type == "Deployment" then 107 | kube.extensions.Deployment 108 | else if type == "DaemonSet" then 109 | kube.extensions.DaemonSet 110 | else 111 | error "unkown pod controller type"; 112 | local labels = config.labels { 113 | tier: config.tier, 114 | }; 115 | local addLabels = kube.addLabels(labels); 116 | addLabels(template { 117 | metadata: { 118 | name: config.name, 119 | namespace: config.namespace, 120 | }, 121 | spec: { 122 | template: addLabels({ 123 | metadata: { 124 | labels: { 125 | version: config.pod.tag, 126 | }, 127 | }, 128 | spec: kube.PodSpec(config), 129 | }), 130 | }, 131 | }), 132 | 133 | Service(config):: kube.v1.ApiVersion { 134 | local labels = config.labels { 135 | tier: config.tier, 136 | }, 137 | kind: "Service", 138 | metadata: { 139 | name: config.name, 140 | namespace: config.namespace, 141 | labels: labels, 142 | }, 143 | spec: { 144 | selector: labels, 145 | ports: [{ 146 | port: config.pod.port, 147 | targetPort: config.pod.port, 148 | }], 149 | }, 150 | }, 151 | 152 | Deployment(config):: 153 | std.mergePatch(kube.PodController("Deployment", config), { 154 | spec: { 155 | selector: { 156 | matchLabels: config.labels { 157 | tier: config.tier, 158 | }, 159 | }, 160 | replicas: config.pod.replicas, 161 | }, 162 | }), 163 | 164 | DaemonSet(config):: 165 | kube.PodController("DaemonSet", config) { 166 | spec+: { 167 | template+: { 168 | spec+: { 169 | nodeSelector: { 170 | minion: "true", 171 | }, 172 | }, 173 | }, 174 | }, 175 | }, 176 | 177 | DefaultConfig(config):: 178 | std.mergePatch({ 179 | pod: { 180 | replicas: 3, 181 | repository: "gcr.io/mikedanese-k8s", 182 | image: config.name, 183 | resourceRequests: { 184 | cpu: "0.1", 185 | }, 186 | port: 80, 187 | privileged: false, 188 | httpLiveness: true, 189 | hostNetwork: false, 190 | hostPID: false, 191 | hostVolumes: [], 192 | secrets: [], 193 | }, 194 | }, config), 195 | } 196 | --------------------------------------------------------------------------------