Repository: kinvolk/lokomotive
Pull request #972 (2020-09-15 11:28:01)

gangway update helm chart
closes: #956

Signed-off-by: knrt10 <kautilya@kinvolk.io>


Patch:
diff --git a/assets/charts/components/gangway/.helmignore b/assets/charts/components/gangway/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/assets/charts/components/gangway/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/assets/charts/components/gangway/Chart.yaml b/assets/charts/components/gangway/Chart.yaml
new file mode 100644
index 00000000..bdfd4999
--- /dev/null
+++ b/assets/charts/components/gangway/Chart.yaml
@@ -0,0 +1,23 @@
+apiVersion: v2
+name: gangway
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+appVersion: v3.2.0
diff --git a/assets/charts/components/gangway/templates/configmap.yaml b/assets/charts/components/gangway/templates/configmap.yaml
new file mode 100644
index 00000000..d69c1881
--- /dev/null
+++ b/assets/charts/components/gangway/templates/configmap.yaml
@@ -0,0 +1,97 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: gangway
+data:
+  gangway.yaml: |
+    # The address to listen on. Defaults to 0.0.0.0 to listen on all interfaces.
+    # Env var: GANGWAY_HOST
+    # host: 0.0.0.0
+
+    # The port to listen on. Defaults to 8080.
+    # Env var: GANGWAY_PORT
+    # port: 8080
+
+    # Should Gangway serve TLS vs. plain HTTP? Default: false
+    # Env var: GANGWAY_SERVE_TLS
+    # serveTLS: false
+
+    # The public cert file (including root and intermediates) to use when serving
+    # TLS.
+    # Env var: GANGWAY_CERT_FILE
+    # certFile: /etc/gangway/tls/tls.crt
+
+    # The private key file when serving TLS.
+    # Env var: GANGWAY_KEY_FILE
+    # keyFile: /etc/gangway/tls/tls.key
+
+    # The cluster name. Used in UI and kubectl config instructions.
+    # Env var: GANGWAY_CLUSTER_NAME
+    clusterName: {{ .Values.clusterName }}
+
+    # OAuth2 URL to start authorization flow.
+    # Env var: GANGWAY_AUTHORIZE_URL
+    authorizeURL: {{ .Values.authorizeURL }}
+
+    # OAuth2 URL to obtain access tokens.
+    # Env var: GANGWAY_TOKEN_URL
+    tokenURL: {{ .Values.tokenURL }}
+
+    # Endpoint that provides user profile information [optional]. Not all providers
+    # will require this.
+    # Env var: GANGWAY_AUDIENCE
+    audience: "https://${DNS_NAME}/userinfo"
+
+    # Used to specify the scope of the requested Oauth authorization.
+    scopes: ["openid", "profile", "email", "offline_access", "groups"]
+
+    # Where to redirect back to. This should be a URL where gangway is reachable.
+    # Typically this also needs to be registered as part of the oauth application
+    # with the oAuth provider.
+    # Env var: GANGWAY_REDIRECT_URL
+    redirectURL: {{ .Values.redirectURL }}
+
+    # API client ID as indicated by the identity provider
+    # Env var: GANGWAY_CLIENT_ID
+    clientID: {{ .Values.clientID }}
+
+    # API client secret as indicated by the identity provider
+    # Env var: GANGWAY_CLIENT_SECRET
+    clientSecret: {{ .Values.clientSecret }}
+
+    # Some identity providers accept an empty client secret, this
+    # is not generally considered a good idea. If you have to use an
+    # empty secret and accept the risks that come with that then you can
+    # set this to true.
+    #allowEmptyClientSecret: false
+
+    # The JWT claim to use as the username. This is used in UI.
+    # Default is "nickname". This is combined with the clusterName
+    # for the "user" portion of the kubeconfig.
+    # Env var: GANGWAY_USERNAME_CLAIM
+    usernameClaim: "email"
+
+    # The JWT claim to use as the email claim
+    emailClaim: "email"
+
+    # The API server endpoint used to configure kubectl
+    # Env var: GANGWAY_APISERVER_URL
+    apiServerURL: {{ .Values.apiServerURL }}
+
+    # The path to find the CA bundle for the API server. Used to configure kubectl.
+    # This is typically mounted into the default location for workloads running on
+    # a Kubernetes cluster and doesn't need to be set.
+    # Env var: GANGWAY_CLUSTER_CA_PATH
+    # cluster_ca_path: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
+
+    # The path to a root CA to trust for self signed certificates at the Oauth2 URLs
+    # Env var: GANGWAY_TRUSTED_CA_PATH
+    #trustedCAPath: /cacerts/rootca.crt
+
+    # The path gangway uses to create urls (defaults to "")
+    # Env var: GANGWAY_HTTP_PATH
+    #httpPath: "https://${GANGWAY_HTTP_PATH}"
+
+    # The path to find custom HTML templates
+    # Env var: GANGWAY_CUSTOM_HTTP_TEMPLATES_DIR
+    customHTMLTemplatesDir: "/theme"
diff --git a/assets/charts/components/gangway/templates/deployment.yaml b/assets/charts/components/gangway/templates/deployment.yaml
new file mode 100644
index 00000000..410503a3
--- /dev/null
+++ b/assets/charts/components/gangway/templates/deployment.yaml
@@ -0,0 +1,81 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: gangway
+  labels:
+    app: gangway
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: gangway
+  strategy:
+  template:
+    metadata:
+      labels:
+        app: gangway
+        revision: "1"
+    spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 65534
+        runAsGroup: 65534
+      initContainers:
+      - name: download-theme
+        image: alpine/git:1.0.7
+        command:
+         - git
+         - clone
+         - "https://github.com/kinvolk/gangway-theme.git"
+         - /theme
+        volumeMounts:
+        - name: theme
+          mountPath: /theme/
+      containers:
+        - name: gangway
+          image: gcr.io/heptio-images/gangway:{{ .Chart.AppVersion }}
+          imagePullPolicy: Always
+          command: ["gangway", "-config", "/gangway/gangway.yaml"]
+          env:
+            - name: GANGWAY_SESSION_SECURITY_KEY
+              valueFrom:
+                secretKeyRef:
+                  name: gangway-key
+                  key: sessionkey
+          ports:
+            - name: http
+              containerPort: 8080
+              protocol: TCP
+          resources:
+            requests:
+              cpu: "100m"
+              memory: "128Mi"
+            limits:
+              cpu: "200m"
+              memory: "512Mi"
+          volumeMounts:
+            - name: gangway
+              mountPath: /gangway/
+            - name: theme
+              mountPath: /theme/
+          livenessProbe:
+            httpGet:
+              path: /
+              port: 8080
+            initialDelaySeconds: 20
+            timeoutSeconds: 1
+            periodSeconds: 60
+            failureThreshold: 3
+          readinessProbe:
+            httpGet:
+              path: /
+              port: 8080
+            timeoutSeconds: 1
+            periodSeconds: 10
+            failureThreshold: 3
+      volumes:
+        - name: gangway
+          configMap:
+            name: gangway
+        - name: theme
+          emptyDir: {}
diff --git a/assets/charts/components/gangway/templates/ingress.yaml b/assets/charts/components/gangway/templates/ingress.yaml
new file mode 100644
index 00000000..b8243a3d
--- /dev/null
+++ b/assets/charts/components/gangway/templates/ingress.yaml
@@ -0,0 +1,20 @@
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+  name: gangway
+  annotations:
+    kubernetes.io/tls-acme: "true"
+    cert-manager.io/cluster-issuer: {{ .Values.certManagerClusterIssuer }}
+    kubernetes.io/ingress.class: contour
+spec:
+  tls:
+  - secretName: gangway
+    hosts:
+    - {{ .Values.ingressHost }}
+  rules:
+  - host: {{ .Values.ingressHost }}
+    http:
+      paths:
+      - backend:
+          serviceName: gangwaysvc
+          servicePort: http
diff --git a/assets/charts/components/gangway/templates/secret.yaml b/assets/charts/components/gangway/templates/secret.yaml
new file mode 100644
index 00000000..5c1fe8ed
--- /dev/null
+++ b/assets/charts/components/gangway/templates/secret.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: gangway-key
+type: Opaque
+data:
+  sessionkey: {{ .Values.sessionKey }}
diff --git a/assets/charts/components/gangway/templates/service.yaml b/assets/charts/components/gangway/templates/service.yaml
new file mode 100644
index 00000000..da8cda8d
--- /dev/null
+++ b/assets/charts/components/gangway/templates/service.yaml
@@ -0,0 +1,15 @@
+kind: Service
+apiVersion: v1
+metadata:
+  name: gangwaysvc
+  labels:
+    app: gangway
+spec:
+  type: ClusterIP
+  ports:
+    - name: "http"
+      protocol: TCP
+      port: 80
+      targetPort: "http"
+  selector:
+    app: gangway
diff --git a/assets/charts/components/gangway/values.yaml b/assets/charts/components/gangway/values.yaml
new file mode 100644
index 00000000..60eb7d53
--- /dev/null
+++ b/assets/charts/components/gangway/values.yaml
@@ -0,0 +1,10 @@
+clusterName: 
+ingressHost:
+sessionKey:
+apiServerURL:
+authorizeURL:
+tokenURL:
+clientID:
+clientSecret:
+redirectURL:
+certManagerClusterIssuer:
diff --git a/pkg/assets/generated_assets.go b/pkg/assets/generated_assets.go
index af17a77a..0c6fa561 100644
--- a/pkg/assets/generated_assets.go
+++ b/pkg/assets/generated_assets.go
@@ -1024,6 +1024,68 @@ var vfsgenAssets = func() http.FileSystem {
 			modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
 			content: []byte(""),
 		},
+		"/charts/components/gangway": &vfsgen۰DirInfo{
+			name:    "gangway",
+			modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+		},
+		"/charts/components/gangway/.helmignore": &vfsgen۰CompressedFileInfo{
+			name:             ".helmignore",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 349,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4c\x8e\x41\x6e\xe3\x30\x0c\x45\xf7\x3c\xc5\x1f\x78\x33\x63\x0c\xe4\x43\x24\xb3\x98\x55\x0b\xa4\xc8\xb6\x90\x6d\x46\x62\x22\x8b\x82\x44\x27\x6d\x17\x3d\x7b\x91\x04\x41\xbb\x79\x20\x3f\xc8\x8f\xd7\xe1\xd9\x9b\x71\xcd\x0d\xa6\x90\x90\xb5\x32\x2e\x91\x33\xc6\x55\xd2\x2c\x39\xa0\xf8\xe9\xe4\x03\x37\x47\x1d\x5e\xa2\x34\xb4\xb5\x14\xad\xd6\xd0\x22\xa7\x84\x90\x74\xc4\xe2\x6d\x8a\x92\xc3\x5f\x54\x4e\xde\xe4\xcc\x28\xde\xe2\x8f\xdc\xe7\x99\x3a\x64\x0e\xde\x44\x33\x7e\x97\xca\x07\x79\xe3\x19\x17\xb1\x88\x5f\x7f\x1c\x9e\x72\x7a\x87\xe6\xdb\xe7\x55\x09\x85\x2b\x92\x64\x76\xe4\xb6\xbb\xd7\x9d\x69\x65\xea\xb0\xd1\x65\xd1\x8c\xfd\x66\x87\x59\x6a\x23\x17\xc4\x86\x1b\xef\xfa\xe4\xc6\x8f\x3a\xdc\xf8\x08\x62\x18\xae\x78\xac\xed\x9c\x87\xef\xa2\xd1\x4f\xa7\xb5\xe0\x20\x89\x1b\xf5\xae\x5d\x0a\xf5\x6e\xf4\x27\xea\x9d\x2d\xd7\x59\xab\x04\xea\x3f\xa9\xc3\xde\x57\xd1\xb5\xe1\xff\xf6\x5f\x23\x57\xaa\x1e\x79\x32\x72\x32\xb3\x1f\xee\xe7\x55\x8f\xe4\xce\x6d\xd2\x99\x07\xfa\x0a\x00\x00\xff\xff\x16\xec\x32\x27\x5d\x01\x00\x00"),
+		},
+		"/charts/components/gangway/Chart.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "Chart.yaml",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 1098,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\xc1\x6e\xdb\x3c\x10\x84\xef\x7c\x8a\x05\x7c\xf0\xff\x03\x85\x92\xa6\x37\xdf\x72\x2b\xd0\xde\x1a\xf4\xbe\x26\x47\xd2\x36\x14\x49\x90\x94\x52\xbd\x7d\xb1\x92\x65\x2b\x2e\x10\xf4\x68\x91\x9a\x99\x6f\x76\x2d\x4e\xf2\x13\xb9\x48\x0c\x27\x9a\x9e\x4c\xe0\x01\x27\xea\x38\x74\x6f\x3c\x1b\x87\x62\xb3\xa4\xba\x9c\x3e\xd3\x57\xf8\x81\x6c\xcf\xb9\x52\x1b\x33\x7d\x1b\xcf\xc8\x01\x15\xc5\x98\x03\x3d\x5f\x4e\x2c\x07\x3a\x83\x20\xb5\x47\x26\x0e\x74\xe4\x94\xbc\x58\x56\x95\x23\xc5\x4c\x4c\x47\x2f\xe7\xcc\x79\x3e\xae\xef\x34\xe6\xa0\x02\xb7\x6b\xeb\xe3\x42\x9c\x41\x4c\x36\x7a\x0f\xbb\x3c\x8f\x2d\x55\x0c\xc9\x73\x45\xa1\xda\xf3\xd5\x2e\xb1\x7d\xe5\x0e\x8e\x24\xd4\x48\xd3\x4a\x04\x47\x9c\x6d\x2f\x13\x8a\x39\x50\x8d\x7a\xd1\x21\xf9\x38\xc3\xad\x9e\xdf\xd7\x1c\x9b\x5f\xca\x71\x12\x07\x1a\x0b\xda\xd1\xd3\x58\xc5\x4b\x15\x14\x4d\xdd\x8e\x61\xc9\x50\x16\xf6\xda\xe3\xc2\xeb\x30\xc1\xc7\x84\xdc\xd0\x4b\x8f\xf9\x98\x41\x12\xac\x1f\x9d\xba\xab\x2f\xab\x27\x82\x43\xb0\xb3\x02\xf0\xdf\x9c\x35\x92\x84\x5f\xb0\x95\x6a\x1f\x0b\x76\xc6\x1c\xdc\xce\x79\x81\x53\xeb\xac\x7a\x59\x42\x67\x0e\x94\x24\xc1\x4b\x40\x73\x4f\xe3\x22\x85\xa8\x01\x5b\x09\x20\x0e\xf3\xae\x3b\xd5\xd5\x01\xa1\x8d\x19\x5a\xa2\xde\x7c\x57\x4f\x9d\x13\x4e\xfb\xb0\x3a\xe4\x97\x5e\x0a\x49\xd9\xe1\x5f\xaa\x6e\xd6\xa3\xcb\x2f\x0a\xe3\x70\x46\xa6\xd2\xc7\xd1\x3b\xd5\x95\x60\x33\x06\x84\x0a\x47\x60\xdb\x53\x95\x01\x34\xc7\x91\x06\x7e\x5d\xb4\x42\xb7\x4d\xe9\x26\xae\x29\x45\xfb\xd9\x72\x7f\xba\x74\x2b\xa1\x5b\xae\x71\x4a\xd7\x04\xe6\x40\x97\x4d\x5e\x17\x07\xbf\x13\xac\xfa\xd5\x48\x6d\xf4\x3e\xbe\xd1\x0f\x0c\x1c\xaa\xd8\xed\xa2\xca\xfc\xd7\xd7\x9a\xca\xe9\xe1\xa1\x60\x98\x90\x9b\x98\xbb\x87\xff\xcd\xb4\xfd\x27\x1e\x9b\xcf\xcd\xe3\x3d\xfa\x1d\xa6\x6e\xe5\x1a\xe6\x3a\xd8\x33\x54\xfb\xda\xe6\xc7\xed\x98\xc3\x3f\xf6\xb3\xb5\xb3\x73\x6a\xde\x43\xeb\x18\x77\xe0\xe6\xf0\x01\xfa\xba\xb0\x5b\x8c\x8c\xd6\xaf\x1b\x78\xe3\xbb\xa7\x92\x42\x63\xd1\x37\x0d\xa7\x74\xfb\x6c\x7c\x69\x9e\x9a\x47\xf3\x27\x00\x00\xff\xff\x63\x59\xab\xe0\x4a\x04\x00\x00"),
+		},
+		"/charts/components/gangway/templates": &vfsgen۰DirInfo{
+			name:    "templates",
+			modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+		},
+		"/charts/components/gangway/templates/configmap.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "configmap.yaml",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 3475,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x57\xdf\x6f\xdb\xb6\x13\x7f\xcf\x5f\x71\xf0\xf7\x0b\xac\x05\x0a\xbb\xdb\x53\xa1\x97\xc1\xb0\xd5\xc6\xab\xe3\x18\x92\xdc\xa2\x2b\x0a\x83\x26\x4f\x16\x61\x9a\xd4\x48\xca\x81\xd7\xe5\x7f\x1f\x8e\x94\x1c\xa5\x89\xb2\x3d\x0c\x41\x10\x89\x3c\xdd\x7d\xee\xee\x73\x3f\xc2\x6a\xf9\x09\xad\x93\x46\x27\x70\xfa\xf9\xea\x20\xb5\x48\x60\x66\x74\x29\xf7\x37\xac\xbe\x3a\xa2\x67\x82\x79\x96\x5c\x01\x68\x76\xc4\x04\xf6\x4c\xef\xef\xd8\xf9\xaa\x3b\x6d\xdf\xc7\x67\x76\x54\x09\xfc\x75\x05\x00\xf0\x3f\x28\x2a\x04\x26\x84\x45\xe7\xc0\x1b\x50\xd2\x79\xd4\x60\xf4\x18\xe6\x58\xb2\x46\xf9\x70\xfc\x76\x1c\x7e\x1e\x49\x00\x53\x0a\xa4\xf6\x68\x4b\xc6\xd1\x8d\x5b\x85\xa9\x3e\xc1\x89\xd9\x04\x3e\x4c\x57\x1f\x3e\x4f\xbf\x6c\xaf\x6f\xf3\xa2\xbd\xab\x8c\xf3\x49\xa7\xec\xaa\x87\xa0\x36\xd6\xbf\x60\xfe\xdd\xdb\x77\x6f\x07\xf5\xaf\x6f\xb3\x4e\x3f\xa9\x49\x82\x74\xa7\x3c\xaf\x4c\xa3\x04\x7c\x88\xbe\x83\x43\x7b\x42\x28\x96\x39\x9c\xdc\x18\x6a\xc5\xa4\x86\xeb\xa2\x58\xff\xda\x99\x4b\xa0\x64\xca\xe1\x90\xad\x3c\xcd\x3e\xa5\xdb\x62\x99\xb7\x02\x41\x5f\xb1\xcc\xbb\xcf\xfa\x2e\x35\x3b\x25\x39\x70\xb4\x1e\x4a\xa9\x10\x5e\x49\xcd\x55\x23\xa4\xde\x83\x35\xc6\x03\xd3\x22\x86\xef\x88\x42\x32\x8f\xee\x35\xb9\xda\x38\x84\xbb\x0a\x75\x50\x2d\xf5\xbe\xd3\xb8\xcc\x07\x03\x30\x4b\xb3\x62\xfb\x7e\xb1\x4c\x5b\x01\x32\xf9\x5e\x2a\x4c\x60\x82\x9e\x4f\xda\xbc\x4f\xbc\x72\xf4\x3b\xe6\xd6\x3f\xc2\x69\xe5\x89\x79\x84\x03\x9e\x23\xce\xbe\xf5\x17\xed\x7e\x4c\xbf\xf4\xcd\x1e\xf0\xfc\x82\xd5\x03\x9e\xfb\x56\xb9\x6a\x9c\x47\x1b\xa8\x3a\x86\x8d\x43\x8a\x05\x6c\x16\x21\x2a\x87\x66\x87\xdc\x2b\xe0\x81\xdd\x20\xb5\xf3\xb6\xe1\x5e\x1a\x3d\xcc\xb2\xd9\x72\x93\x17\x69\xb6\x5d\x4d\x6f\x22\xa0\xd6\xc2\x2a\xd4\xc2\xf7\xef\x30\xfe\xc4\x54\x83\x6e\xdc\x3b\x87\xfb\xfb\x0e\xd3\xed\xb4\xf1\xd5\x2f\xb0\xc9\x96\x94\x04\xe7\x99\xf5\xc0\x1a\x5f\x19\x2b\xff\x64\x64\x19\x4a\x65\xee\x06\xad\x4f\x37\xc5\xf5\x6d\xb6\xf8\x3d\xdd\x6e\xb2\x65\x10\xea\x3e\xc6\x4d\xb6\x7c\x64\xbf\x7f\x31\x08\xc0\xec\x3c\x51\x93\x71\x1e\xeb\xf2\x80\x2f\xb8\x5e\xdc\x7e\x4c\x57\x17\xc3\x41\xf8\x47\xa3\xdd\x61\xcf\x60\xaa\x45\x6d\xa4\xf6\xe0\x2b\xe6\xa1\xb6\xe6\x24\x05\x3a\xe2\x9f\xa5\xb7\x40\x05\xa9\x4b\x63\x8f\xd1\xff\xaf\xa6\xa6\xbf\x4c\x7d\x1b\xc3\x8a\xe8\xab\x54\xf7\x95\x75\xad\xce\x3b\xa9\x14\x58\xfc\xa3\x91\x16\xc1\x57\x72\x18\xf3\x74\x33\x5f\xa4\xab\x59\xda\xc6\x4a\x48\xd4\x1c\x13\x18\x55\xde\xd7\x2e\x99\x4c\xfe\xff\x7d\xbe\xca\x43\x32\xef\x27\x04\x89\x90\x8c\x3a\xe8\x81\x2e\x94\xa6\x1a\xb9\x2c\xcf\xe0\x2b\x04\xc7\x4d\x8d\x60\xca\xf0\x42\x10\xd0\x79\x14\x70\x4b\xe1\x7e\x9c\xc9\x88\x29\xc8\xbb\x04\xbe\x8e\x4c\x8d\x5a\x8a\xd1\x1b\x18\xb5\x6e\xd3\x23\x1e\x99\x54\xf4\x60\xca\x52\x49\x8d\xdb\x98\x0a\x3a\xd9\x5b\xd3\xd4\x6e\xf4\xad\x43\xf3\xb9\x42\xf2\xd6\x80\x45\x21\x2d\x72\x0f\x3b\xc6\x0f\xe0\xcd\x18\x8a\x4a\x3a\x70\xb1\x05\xed\x10\x58\x48\xef\x5d\x90\x6f\xeb\x03\xa4\x03\x8b\x8c\x57\x6c\xa7\xb0\x8b\x56\x71\xae\x25\x67\x4a\x9d\x43\x0c\x81\x29\x67\x40\x23\x8a\xd0\x0b\x77\xe4\xde\x9e\xfa\xa4\x45\x01\xcc\x41\x4d\x5c\x6d\x1d\x37\xd1\xdd\xba\x56\x92\x07\x67\x2f\x89\xf1\x55\x14\x20\x9a\x5d\xf2\x36\x98\x9e\x2c\x9d\x2f\xb2\x74\x56\x5c\x58\xd5\xf9\xf6\x23\xb1\x7a\xe7\x3d\x6e\x4d\xd7\x0b\xe0\x4a\xa2\xf6\xb0\x98\x13\x46\xa9\x05\x01\x42\x01\xbb\x98\x2e\x29\x50\x7b\xe9\xcf\x17\x28\xc3\x75\xbd\x48\x57\xc5\x76\x31\x6f\x8b\x9a\x94\x2e\xe6\x3f\x54\x74\x3c\x7c\x1e\x80\x43\x6e\xd1\xff\x27\x20\xf2\x74\x96\xa5\x45\x0f\x48\x1e\x74\x3f\x03\x26\x5e\xf4\x00\xe5\xe6\xf8\x8c\x3d\x17\x2a\xbc\xa6\x59\x00\x78\xac\xfd\xf9\x31\xe8\x37\x21\xff\xad\x06\xe9\x40\x1b\x0f\x7b\xd4\x68\x03\x37\xb8\xd1\x8e\x94\x10\x0b\x60\x6f\x8c\x20\xfd\x6c\x0c\x8b\x12\xce\xa6\x81\x8a\x9d\xb0\x1b\x28\xac\xe3\x41\x34\xd2\x85\x44\x8b\xce\x7e\x28\x1a\xe9\x0e\x2e\x76\x03\x4e\x68\x5b\xce\xb0\x70\xab\x83\x4e\x7e\x51\xe4\xd0\x47\x72\x7a\x03\xde\x36\x1d\x75\x99\x52\xe6\x2e\x25\x23\xb3\x47\x01\x7a\x32\x1d\x7f\xfb\x5c\x00\x57\x4c\x1e\x2f\x10\x5d\x00\x41\xb5\x1e\x67\x42\xa8\x1d\x19\x1a\x52\x3b\x1b\x3a\xb6\xb6\xa3\x9a\x2e\x47\x5a\xf2\x03\xc9\x8f\x1e\x3e\xe0\xe6\xb8\x93\x1a\xc5\x03\xe9\x7b\x2d\xbf\x55\x51\x1a\x1b\x6e\x46\x64\x6f\x14\xf6\x06\xea\x71\x6d\x15\x85\xf9\x13\x66\xcf\x60\x81\x6c\xf2\x34\xa3\xee\xb4\x9d\x2d\xa7\x8b\x9b\x20\xd5\x41\x9f\x91\x5b\x49\xd7\x42\xfe\x8d\xd3\x41\x32\xde\x04\xe9\xf0\x3e\xac\x86\xc8\x1d\x16\x0f\x0b\xd8\x35\xf1\xa6\x6d\x89\x11\x76\x63\xb1\x1b\xa2\x83\x0d\x78\xbd\x08\xcb\x4c\xf6\x30\xb1\x6a\x99\x07\xad\x4f\x26\x56\xef\xa2\x47\xe9\xb0\x3c\x30\x0a\xb1\x81\x52\x6a\x11\x5c\x99\x4d\x61\xd7\x68\xa1\xf0\x12\xe2\x07\xb4\xe3\x4b\xe3\x7e\x82\xf2\xd2\xf9\xda\x1c\xfa\x4b\x07\x3c\x9a\x46\xfb\xc0\x00\x62\x5a\x85\x20\xda\xec\x2b\xc3\xdb\xc1\x6c\x2c\xdc\x19\x7b\x50\x86\x09\x07\xb6\xd1\x9a\x16\x97\x4b\xeb\x63\xf0\xb1\xd9\xa1\xd5\xe8\xd1\x5d\xd6\x0e\xa2\xbe\x30\xe8\xf4\x4f\x3e\x74\xd6\xb6\xb1\x3a\xf4\xff\xb8\x60\xcc\xa6\xdb\xf5\xb4\xb8\xee\x76\xad\xa8\x70\xcb\xd9\x96\x62\x91\xc0\x68\x72\x62\x76\x62\x1b\x3d\x89\x55\xe6\x26\x87\x8b\xf9\xb1\x34\x93\xb0\x58\x71\x64\x9c\x93\x63\x13\xce\x68\x1f\x1b\x3d\x17\x53\x16\xd7\xc4\xd9\xb4\xad\x31\xe7\x83\xab\x0e\x55\x09\x4e\xee\x89\xe1\xb4\xe9\xc9\x32\x34\x35\x07\xb1\x50\xe3\xc4\x0b\x8b\x84\x1b\x5c\x18\x32\x72\x65\xfe\xd8\x95\x60\x01\xc5\x6c\xba\x0e\x7e\x4c\x38\x23\xed\x6e\x42\x20\x22\xca\x27\x20\xbb\x19\xd6\x38\x0c\x8d\x80\x5b\xa4\x4d\xb2\xb1\xca\xc1\x2b\xd1\xdb\xdf\x47\xa3\xd7\x83\xff\x1d\x14\xc5\xba\x87\x82\xe6\x7f\x04\xd0\x5b\x05\x9e\xc8\xde\x3f\x1b\xb0\x40\x42\xde\x38\x6f\x8e\x70\x5d\xdc\x2c\xc1\xe3\xb1\x56\x14\x9b\xc1\x9c\x6e\xf2\xe2\xf6\x26\xea\x2d\xd2\x9b\xf5\x72\x5a\xa4\xf9\x76\xbe\xc8\x62\x8f\x0f\xaa\x48\x53\xd1\x29\x9a\x4b\x4b\x29\xf6\x15\x1e\x71\x74\xf5\x77\x00\x00\x00\xff\xff\xec\x03\x49\x10\x93\x0d\x00\x00"),
+		},
+		"/charts/components/gangway/templates/deployment.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "deployment.yaml",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 2010,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x54\xc1\x6e\xe3\x36\x10\xbd\xfb\x2b\x06\x3a\xd7\x96\x9d\x6d\xb6\x01\x6f\x46\x92\x06\x8b\x6d\x52\x23\xce\xb6\x08\x8a\x22\x60\xa4\x89\x44\x98\xe4\xb0\xe4\xc8\xa9\x10\xe4\xdf\x0b\x45\x96\x2d\x72\xbd\x8b\xf4\xb0\xba\x58\x9e\x37\xf3\xf8\x66\xe6\x51\xd2\xa9\x3f\xd0\x07\x45\x56\x80\x74\x2e\xe4\xdb\xc5\x64\xa3\x6c\x29\xe0\x02\x9d\xa6\xd6\xa0\xe5\x89\x41\x96\xa5\x64\x29\x26\x00\x56\x1a\x14\x50\x49\x5b\x3d\xcb\x76\x02\xa0\xe5\x23\xea\xd0\x21\xd0\x11\x1c\xa0\xe0\xb0\xe8\xc2\x1e\x9d\x56\x85\x0c\x02\x16\x13\x80\x80\x1a\x0b\x26\xdf\x17\x18\xc9\x45\xfd\xdb\x88\x21\xe1\x00\x08\xec\x25\x63\xd5\x76\x30\xa3\x71\x5a\x32\xee\x6a\x47\xa2\xba\x47\x47\x34\x5f\x11\xf5\x8f\xc7\xad\xea\x9b\xcd\x16\xd9\x5b\x74\x90\xf9\xf6\x8e\x45\xe3\x15\xb7\xe7\x64\x19\xff\xe5\x03\x97\x6f\xec\x32\xdc\x90\xbd\x25\x62\x01\xec\x1b\x8c\xa1\x2f\x01\xbd\x80\x8f\xa7\xa7\x1f\x7e\x8e\x81\x2b\x4f\x8d\x8b\x11\x65\x15\x77\x07\x48\x65\xd1\xef\xf5\x4e\x77\x83\x2d\xe9\xd9\x6a\x92\xe5\x94\x6b\x34\x87\x53\x94\x91\x15\x0a\x90\xda\x29\x8b\x79\xa5\x58\x2c\x66\xf3\xd9\x2f\x7b\xbc\x20\x63\xa4\x2d\x0f\x8a\x61\x0a\x95\xe2\xf1\xdf\x42\x93\xc5\x71\x20\xab\x99\x5d\x10\x79\x47\x57\x37\x8f\xb3\x82\x4c\xbe\x51\x76\x4b\x7a\x93\xef\xe6\xd6\xab\x98\x55\x8a\xb3\x71\x65\x1e\x8b\xdb\x92\x6e\x0c\x5e\x53\x63\x79\x34\xff\xa1\xa3\x38\x17\xc0\x74\x79\x2b\xc9\xb5\xd8\xf1\xe4\x93\xa1\x87\x74\x28\x07\x92\x74\x8f\xfb\x89\x54\x85\x9f\x29\xca\x6b\x74\xac\x68\xfa\x16\x0c\x83\x7a\xf1\xf2\x02\xb3\xf3\x5a\x7a\x9e\x2d\x9d\xdb\xd9\x1c\x5e\x5f\x53\x96\x55\xa3\xf5\x8a\xb4\x2a\x5a\x01\x4b\xfd\x2c\xdb\x30\xca\x18\x26\x0b\x7f\x65\x3b\xda\xec\x27\xc8\xa6\x05\xd9\x27\x55\x75\xaf\xc3\x69\xc3\xef\xac\x95\x46\x67\x7f\x8f\x28\xd0\x6e\x47\x8b\x19\x75\x75\xb5\xbc\xb9\xfa\x73\x79\xff\xb0\xbe\x5c\xaf\x3f\xfd\x7e\xf3\xb0\xbe\x3c\xff\x72\xfb\xe9\xee\xfe\xe1\xf3\xe5\x7d\x54\x00\xb0\x95\xba\xc1\x5f\x3d\x19\x91\x00\x6f\xae\xf5\xc8\x9f\xb1\xbd\xc5\xa7\xaf\xd1\xe4\xc6\x4e\x37\xd8\x1e\xc9\xd9\x60\x2b\x20\x60\xe8\x26\x14\x67\x38\xf2\xe3\xad\x8e\xe5\x77\xfe\x49\xb8\xf6\x3b\x5c\x91\x67\x01\x67\xf3\xb3\x79\x92\xe1\x3c\x31\x15\xa4\x05\xdc\x9d\xaf\x46\x98\xc7\x40\x8d\x2f\x30\x39\xcb\xe3\x3f\x0d\x86\x54\x01\x40\xe1\x9a\xee\x0e\xcf\xe7\x26\x4b\x10\x83\x86\x7c\xdb\x81\x27\x67\xd7\x2a\x46\xb5\x32\xea\x5b\x5c\x27\xdf\xe3\x3a\x5d\x9c\xc4\x5c\xc7\x3d\x0f\xdf\xb5\x2c\x24\xee\x1f\x8c\x73\xb4\x3c\xbd\x36\x69\x71\x74\x75\xfa\xde\xb6\x68\x31\x84\x95\xa7\x47\x8c\x25\x75\x8b\xba\x42\x4e\xfb\x76\x3d\x53\x1a\x3d\xbe\xb9\xee\xb3\xa5\xa4\xbe\x40\x2d\xdb\x35\x16\x64\xcb\x20\xe0\x24\xce\x61\x65\x90\x1a\xde\xc3\x8b\x08\x75\xe8\x15\x95\x7b\xf0\x63\x5c\xfb\x24\x95\x6e\x3c\xde\xd5\x1e\x43\x4d\xba\x14\xf0\x21\x72\x87\x2c\xd5\x0f\xec\xee\x7f\x29\x5f\xbc\x57\x79\x6f\x92\x77\x7d\xce\xfa\xef\xc9\xb5\x74\x71\x1b\xc7\xd3\xbf\xe5\x11\x34\x8e\xdb\x0b\xe5\x05\xbc\xbc\x4e\xfe\x0b\x00\x00\xff\xff\x6c\x88\xb7\x02\xda\x07\x00\x00"),
+		},
+		"/charts/components/gangway/templates/ingress.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "ingress.yaml",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 471,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8f\xbd\x6e\xeb\x30\x0c\x85\x77\x3d\x05\x91\x5d\xbe\xc8\x76\xa1\xb5\x4b\x33\xb4\xe8\x94\x9d\x51\x08\x47\xb0\x4c\x19\x24\xe5\xa0\x08\xf2\xee\x85\x6c\x07\xe8\x1f\xaa\x4d\xd2\xc7\xc3\xef\xe0\x94\x8e\x24\x9a\x0a\x07\x60\xb2\x6b\x91\x21\x71\xdf\x0d\xff\xb5\x4b\xe5\xdf\xbc\x3f\x91\xe1\xde\x0d\x89\xcf\x01\x0e\xdc\x0b\xa9\xba\x91\x0c\xcf\x68\x18\x1c\x00\xe3\x48\x01\x7a\xe4\xfe\x8a\xef\x0e\x00\x99\x8b\xa1\xa5\xc2\xda\xbe\x01\x86\x7a\x22\x61\x32\x5a\x02\x2d\xab\xc7\xd8\x46\x76\x26\x95\x76\x0b\x12\x49\xcc\x8f\xc8\xd8\x93\x34\x28\xe6\xaa\x46\xe2\x93\x6a\x25\x09\x70\xbb\x41\x77\xc4\x5c\x49\xbb\x86\xbe\xac\xe4\xd3\x4a\x1d\x16\x08\xee\xf7\x5f\xb6\xa5\x55\xb8\x8b\x19\x55\x03\xc4\xc2\x56\xaa\x38\x9d\x28\x36\x39\xcb\x8b\xa3\x07\xa5\x28\x64\xaf\xdf\xaa\x00\x5c\x8a\xda\x56\xc3\x7f\xb6\xd8\x72\x9f\x8b\xda\xba\x58\x6a\xa6\x2d\xab\xcd\x84\x3f\x61\x80\x8b\xd9\xb4\xc6\x02\x4c\x68\x17\x7d\x5c\x3c\x9c\x30\x0e\xc4\xe7\xc7\x43\x3b\x4a\x32\xa7\x48\x5f\xf4\x74\x8e\x3f\x89\xb7\x22\x16\x96\x70\xf7\x11\x00\x00\xff\xff\x2c\x56\xf2\x65\xd7\x01\x00\x00"),
+		},
+		"/charts/components/gangway/templates/secret.yaml": &vfsgen۰FileInfo{
+			name:    "secret.yaml",
+			modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			content: []byte("\x61\x70\x69\x56\x65\x72\x73\x69\x6f\x6e\x3a\x20\x76\x31\x0a\x6b\x69\x6e\x64\x3a\x20\x53\x65\x63\x72\x65\x74\x0a\x6d\x65\x74\x61\x64\x61\x74\x61\x3a\x0a\x20\x20\x6e\x61\x6d\x65\x3a\x20\x67\x61\x6e\x67\x77\x61\x79\x2d\x6b\x65\x79\x0a\x74\x79\x70\x65\x3a\x20\x4f\x70\x61\x71\x75\x65\x0a\x64\x61\x74\x61\x3a\x0a\x20\x20\x73\x65\x73\x73\x69\x6f\x6e\x6b\x65\x79\x3a\x20\x7b\x7b\x20\x2e\x56\x61\x6c\x75\x65\x73\x2e\x73\x65\x73\x73\x69\x6f\x6e\x4b\x65\x79\x20\x7d\x7d\x0a"),
+		},
+		"/charts/components/gangway/templates/service.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "service.yaml",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 226,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\xce\x31\x0b\xc2\x30\x10\x05\xe0\x3d\xbf\xe2\xd1\x5d\xd0\x4d\xb2\x76\x72\x2b\x28\xee\x67\x7a\xd4\x60\x9a\x1c\x97\xb3\xd2\x7f\x2f\x2d\xb5\x93\xe3\x7d\x8f\x77\xbc\x57\xcc\xbd\xc7\x95\x75\x8a\x81\x1d\x49\xbc\xb3\xd6\x58\xb2\xc7\x74\x72\x23\x1b\xf5\x64\xe4\x1d\x90\x69\x64\x8f\x81\xf2\xf0\xa1\xb9\x4e\xc1\x01\x89\x1e\x9c\xea\x12\x02\x24\xb2\xa7\xae\x0a\x87\x85\x6d\x16\xf6\x68\xd3\xbb\x1a\xeb\xa5\x73\x80\x14\xb5\xad\x71\xd8\x5e\x36\x4f\x33\x69\x56\x02\x44\x8b\x95\x50\x92\xc7\xad\xed\x7e\x56\xd4\x3c\xce\xc7\xed\x34\xd2\x81\xad\x5b\x71\xef\x56\x4e\x1c\xac\xe8\x9f\x31\xdf\x00\x00\x00\xff\xff\x31\xaa\xa8\x94\xe2\x00\x00\x00"),
+		},
+		"/charts/components/gangway/values.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "values.yaml",
+			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
+			uncompressedSize: 140,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\xcd\x4b\x0a\xc2\x40\x10\x84\xe1\x7d\x9f\xc2\x73\xf4\x56\x17\x06\x1f\x0b\x83\x07\x18\xc6\x22\x36\xc6\x1e\xa9\xee\x11\xf4\xf4\x42\x82\xbb\xaf\xa0\xe0\xaf\x73\x8f\x04\xcf\xe5\x09\xdd\x88\xf9\x44\x44\xec\x5b\xa4\x4a\x20\xc2\x9a\x1f\xf0\x51\x29\x2f\x1b\xc1\x37\x78\xbd\x1c\x55\x4a\xcf\x7b\xa3\x7d\xb1\xac\x6c\x0f\xf8\xa2\x3a\x1b\x3c\x87\xdd\x5f\x23\x2a\x91\x2a\xc4\xcd\x88\x9a\xeb\x09\xcc\x53\xf1\x32\x81\xdb\x35\x3e\x44\x74\x50\xe5\x17\x00\x00\xff\xff\x68\xbe\xdb\x11\x8c\x00\x00\x00"),
+		},
 		"/charts/components/httpbin": &vfsgen۰DirInfo{
 			name:    "httpbin",
 			modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
@@ -6032,6 +6094,7 @@ var vfsgenAssets = func() http.FileSystem {
 		fs["/charts/components/dex"].(os.FileInfo),
 		fs["/charts/components/external-dns"].(os.FileInfo),
 		fs["/charts/components/flatcar-linux-update-operator"].(os.FileInfo),
+		fs["/charts/components/gangway"].(os.FileInfo),
 		fs["/charts/components/httpbin"].(os.FileInfo),
 		fs["/charts/components/istio-operator"].(os.FileInfo),
 		fs["/charts/components/linkerd2"].(os.FileInfo),
@@ -6217,6 +6280,19 @@ var vfsgenAssets = func() http.FileSystem {
 		fs["/charts/components/flatcar-linux-update-operator/templates/update-operator-sa.yaml"].(os.FileInfo),
 		fs["/charts/components/flatcar-linux-update-operator/templates/update-operator.yaml"].(os.FileInfo),
 	}
+	fs["/charts/components/gangway"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
+		fs["/charts/components/gangway/.helmignore"].(os.FileInfo),
+		fs["/charts/components/gangway/Chart.yaml"].(os.FileInfo),
+		fs["/charts/components/gangway/templates"].(os.FileInfo),
+		fs["/charts/components/gangway/values.yaml"].(os.FileInfo),
+	}
+	fs["/charts/components/gangway/templates"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
+		fs["/charts/components/gangway/templates/configmap.yaml"].(os.FileInfo),
+		fs["/charts/components/gangway/templates/deployment.yaml"].(os.FileInfo),
+		fs["/charts/components/gangway/templates/ingress.yaml"].(os.FileInfo),
+		fs["/charts/components/gangway/templates/secret.yaml"].(os.FileInfo),
+		fs["/charts/components/gangway/templates/service.yaml"].(os.FileInfo),
+	}
 	fs["/charts/components/httpbin"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
 		fs["/charts/components/httpbin/.helmignore"].(os.FileInfo),
 		fs["/charts/components/httpbin/Chart.yaml"].(os.FileInfo),
diff --git a/pkg/components/gangway/component.go b/pkg/components/gangway/component.go
index 5eb18e34..2c4aa9e3 100644
--- a/pkg/components/gangway/component.go
+++ b/pkg/components/gangway/component.go
@@ -12,270 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package dex
+package gangway
 
 import (
-	"bytes"
 	"fmt"
-	"text/template"
 
 	"github.com/hashicorp/hcl/v2"
 	"github.com/hashicorp/hcl/v2/gohcl"
 
+	internaltemplate "github.com/kinvolk/lokomotive/internal/template"
 	"github.com/kinvolk/lokomotive/pkg/components"
+	"github.com/kinvolk/lokomotive/pkg/components/util"
 	"github.com/kinvolk/lokomotive/pkg/k8sutil"
 )
 
 const name = "gangway"
 
-const namespaceManifest = `apiVersion: v1
-kind: Namespace
-metadata:
-  name: gangway
-`
-
-const configMapTmpl = `apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: gangway
-  namespace: gangway
-data:
-  gangway.yaml: |
-    # The address to listen on. Defaults to 0.0.0.0 to listen on all interfaces.
-    # Env var: GANGWAY_HOST
-    # host: 0.0.0.0
-
-    # The port to listen on. Defaults to 8080.
-    # Env var: GANGWAY_PORT
-    # port: 8080
-
-    # Should Gangway serve TLS vs. plain HTTP? Default: false
-    # Env var: GANGWAY_SERVE_TLS
-    # serveTLS: false
-
-    # The public cert file (including root and intermediates) to use when serving
-    # TLS.
-    # Env var: GANGWAY_CERT_FILE
-    # certFile: /etc/gangway/tls/tls.crt
-
-    # The private key file when serving TLS.
-    # Env var: GANGWAY_KEY_FILE
-    # keyFile: /etc/gangway/tls/tls.key
-
-    # The cluster name. Used in UI and kubectl config instructions.
-    # Env var: GANGWAY_CLUSTER_NAME
-    clusterName: {{ .ClusterName }}
-
-    # OAuth2 URL to start authorization flow.
-    # Env var: GANGWAY_AUTHORIZE_URL
-    authorizeURL: {{ .AuthorizeURL }}
-
-    # OAuth2 URL to obtain access tokens.
-    # Env var: GANGWAY_TOKEN_URL
-    tokenURL: {{ .TokenURL }}
-
-    # Endpoint that provides user profile information [optional]. Not all providers
-    # will require this.
-    # Env var: GANGWAY_AUDIENCE
-    audience: "https://${DNS_NAME}/userinfo"
-
-    # Used to specify the scope of the requested Oauth authorization.
-    scopes: ["openid", "profile", "email", "offline_access", "groups"]
-
-    # Where to redirect back to. This should be a URL where gangway is reachable.
-    # Typically this also needs to be registered as part of the oauth application
-    # with the oAuth provider.
-    # Env var: GANGWAY_REDIRECT_URL
-    redirectURL: {{ .RedirectURL }}
-
-    # API client ID as indicated by the identity provider
-    # Env var: GANGWAY_CLIENT_ID
-    clientID: {{ .ClientID }}
-
-    # API client secret as indicated by the identity provider
-    # Env var: GANGWAY_CLIENT_SECRET
-    clientSecret: {{ .ClientSecret }}
-
-    # Some identity providers accept an empty client secret, this
-    # is not generally considered a good idea. If you have to use an
-    # empty secret and accept the risks that come with that then you can
-    # set this to true.
-    #allowEmptyClientSecret: false
-
-    # The JWT claim to use as the username. This is used in UI.
-    # Default is "nickname". This is combined with the clusterName
-    # for the "user" portion of the kubeconfig.
-    # Env var: GANGWAY_USERNAME_CLAIM
-    usernameClaim: "email"
-
-    # The JWT claim to use as the email claim
-    emailClaim: "email"
-
-    # The API server endpoint used to configure kubectl
-    # Env var: GANGWAY_APISERVER_URL
-    apiServerURL: {{ .APIServerURL }}
-
-    # The path to find the CA bundle for the API server. Used to configure kubectl.
-    # This is typically mounted into the default location for workloads running on
-    # a Kubernetes cluster and doesn't need to be set.
-    # Env var: GANGWAY_CLUSTER_CA_PATH
-    # cluster_ca_path: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
-
-    # The path to a root CA to trust for self signed certificates at the Oauth2 URLs
-    # Env var: GANGWAY_TRUSTED_CA_PATH
-    #trustedCAPath: /cacerts/rootca.crt
-
-    # The path gangway uses to create urls (defaults to "")
-    # Env var: GANGWAY_HTTP_PATH
-    #httpPath: "https://${GANGWAY_HTTP_PATH}"
-
-    # The path to find custom HTML templates
-    # Env var: GANGWAY_CUSTOM_HTTP_TEMPLATES_DIR
-    customHTMLTemplatesDir: "/theme"
-`
-
-const deploymentManifest = `apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: gangway
-  namespace: gangway
-  labels:
-    app: gangway
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: gangway
-  strategy:
-  template:
-    metadata:
-      labels:
-        app: gangway
-        revision: "1"
-    spec:
-      securityContext:
-        runAsNonRoot: true
-        runAsUser: 65534
-        runAsGroup: 65534
-      initContainers:
-      - name: download-theme
-        image: alpine/git:1.0.7
-        command:
-         - git
-         - clone
-         - "https://github.com/kinvolk/gangway-theme.git"
-         - /theme
-        volumeMounts:
-        - name: theme
-          mountPath: /theme/
-      containers:
-        - name: gangway
-          image: gcr.io/heptio-images/gangway:v3.2.0
-          imagePullPolicy: Always
-          command: ["gangway", "-config", "/gangway/gangway.yaml"]
-          env:
-            - name: GANGWAY_SESSION_SECURITY_KEY
-              valueFrom:
-                secretKeyRef:
-                  name: gangway-key
-                  key: sessionkey
-          ports:
-            - name: http
-              containerPort: 8080
-              protocol: TCP
-          resources:
-            requests:
-              cpu: "100m"
-              memory: "128Mi"
-            limits:
-              cpu: "200m"
-              memory: "512Mi"
-          volumeMounts:
-            - name: gangway
-              mountPath: /gangway/
-            - name: theme
-              mountPath: /theme/
-          livenessProbe:
-            httpGet:
-              path: /
-              port: 8080
-            initialDelaySeconds: 20
-            timeoutSeconds: 1
-            periodSeconds: 60
-            failureThreshold: 3
-          readinessProbe:
-            httpGet:
-              path: /
-              port: 8080
-            timeoutSeconds: 1
-            periodSeconds: 10
-            failureThreshold: 3
-      volumes:
-        - name: gangway
-          configMap:
-            name: gangway
-        - name: theme
-          emptyDir: {}
-`
-
-const serviceManifest = `
-kind: Service
-apiVersion: v1
-metadata:
-  name: gangwaysvc
-  namespace: gangway
-  labels:
-    app: gangway
-spec:
-  type: ClusterIP
-  ports:
-    - name: "http"
-      protocol: TCP
-      port: 80
-      targetPort: "http"
-  selector:
-    app: gangway
-`
-
-const ingressTmpl = `apiVersion: networking.k8s.io/v1beta1
-kind: Ingress
-metadata:
-  name: gangway
-  namespace: gangway
-  labels:
-    app.kubernetes.io/managed-by: Helm
-  annotations:
-    kubernetes.io/tls-acme: "true"
-    cert-manager.io/cluster-issuer: {{ .CertManagerClusterIssuer }}
-    kubernetes.io/ingress.class: contour
-    meta.helm.sh/release-name: gangway
-    meta.helm.sh/release-namespace: gangway
-spec:
-  tls:
-  - secretName: gangway
-    hosts:
-    - {{ .IngressHost }}
-  rules:
-  - host: {{ .IngressHost }}
-    http:
-      paths:
-      - backend:
-          serviceName: gangwaysvc
-          servicePort: http
-`
-
-const secretTmpl = `
-apiVersion: v1
-kind: Secret
-metadata:
-  name: gangway-key
-  namespace: gangway
-type: Opaque
-data:
-  sessionkey: {{ .SessionKey }}
-`
-
-func init() {
+func init() { //nolint:gocheckinit
 	components.Register(name, newComponent())
 }
 
@@ -308,43 +61,24 @@ func (c *component) LoadConfig(configBody *hcl.Body, evalContext *hcl.EvalContex
 	return gohcl.DecodeBody(*configBody, evalContext, c)
 }
 
-// TODO: Convert to Helm chart.
 func (c *component) RenderManifests() (map[string]string, error) {
-	tmpl, err := template.New("config-map").Parse(configMapTmpl)
+	helmChart, err := components.Chart(name)
 	if err != nil {
-		return nil, fmt.Errorf("parsing ConfigMap template: %w", err)
-	}
-	var configMapBuf bytes.Buffer
-	if err := tmpl.Execute(&configMapBuf, c); err != nil {
-		return nil, fmt.Errorf("executing ConfigMap template: %w", err)
+		return nil, fmt.Errorf("retrieving chart from assets: %w", err)
 	}
 
-	tmpl, err = template.New("ingress").Parse(ingressTmpl)
+	values, err := internaltemplate.Render(chartValuesTmpl, c)
 	if err != nil {
-		return nil, fmt.Errorf("parsing Ingress template: %w", err)
-	}
-	var ingressBuf bytes.Buffer
-	if err := tmpl.Execute(&ingressBuf, c); err != nil {
-		return nil, fmt.Errorf("executing Ingress template: %w", err)
+		return nil, fmt.Errorf("rendering values template failed: %w", err)
 	}
 
-	tmpl, err = template.New("secret").Parse(secretTmpl)
+	// Generate YAML for the gangway deployment.
+	renderedFiles, err := util.RenderChart(helmChart, name, c.Metadata().Namespace.Name, values)
 	if err != nil {
-		return nil, fmt.Errorf("parsing Secret template: %w", err)
-	}
-	var secretBuf bytes.Buffer
-	if err := tmpl.Execute(&secretBuf, c); err != nil {
-		return nil, fmt.Errorf("executing Secret template: %w", err)
+		return nil, fmt.Errorf("rendering chart failed: %w", err)
 	}
 
-	return map[string]string{
-		"namespace.yml":  namespaceManifest,
-		"config-map.yml": configMapBuf.String(),
-		"deployment.yml": deploymentManifest,
-		"service.yml":    serviceManifest,
-		"ingress.yml":    ingressBuf.String(),
-		"secret.yml":     secretBuf.String(),
-	}, nil
+	return renderedFiles, nil
 }
 
 func (c *component) Metadata() components.Metadata {
diff --git a/pkg/components/gangway/template.go b/pkg/components/gangway/template.go
new file mode 100644
index 00000000..743001fd
--- /dev/null
+++ b/pkg/components/gangway/template.go
@@ -0,0 +1,38 @@
+// Copyright 2020 The Lokomotive Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package gangway has code related to deployment of gangway component.
+package gangway
+
+const chartValuesTmpl = `
+clusterName: {{ .ClusterName }}
+
+ingressHost: {{ .IngressHost }}
+
+sessionKey: {{ .SessionKey }}
+
+apiServerURL: {{ .APIServerURL }}
+
+authorizeURL: {{ .AuthorizeURL }}
+
+tokenURL: {{ .TokenURL }}
+
+clientID: {{ .ClientID }}
+
+clientSecret: {{ .ClientSecret }}
+
+redirectURL: {{ .RedirectURL }}
+
+certManagerClusterIssuer: {{ .CertManagerClusterIssuer }}
+`


Test patch:
diff --git a/pkg/components/gangway/component_test.go b/pkg/components/gangway/component_test.go
new file mode 100644
index 00000000..2f5b6f1d
--- /dev/null
+++ b/pkg/components/gangway/component_test.go
@@ -0,0 +1,89 @@
+// Copyright 2020 The Lokomotive Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package gangway_test
+
+import (
+	"testing"
+
+	"github.com/kinvolk/lokomotive/pkg/components"
+	"github.com/kinvolk/lokomotive/pkg/components/util"
+)
+
+const name = "gangway"
+
+func TestRenderManifest(t *testing.T) { //nolint:funlen
+	tests := []struct {
+		desc    string
+		hcl     string
+		wantErr bool
+	}{
+		{
+			desc: "Valid config",
+			hcl: `
+component "gangway" {
+  cluster_name   = "lokomotive"
+  ingress_host   = "foo"
+  session_key    = "sessionkey"
+  api_server_url = "apiserverurl"
+  authorize_url  = "authorizeurl"
+  token_url      = "tokenurl"
+  client_id      = "clientid"
+  client_secret  = "clientsecret"
+  redirect_url   = "redirecturl"
+}
+			`,
+		},
+		{
+			desc: "invalid config",
+			hcl: `
+component "gangway" {
+  certmanager_cluster_issuer = "letsencrypt-staging"
+}
+			`,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range tests {
+		b, d := util.GetComponentBody(tc.hcl, name)
+		if d != nil {
+			t.Errorf("%s - Error getting component body: %v", tc.desc, d)
+		}
+
+		c, err := components.Get(name)
+		if err != nil {
+			t.Fatalf("failed getting component: %v", err)
+		}
+
+		d = c.LoadConfig(b, nil)
+
+		if !tc.wantErr && d.HasErrors() {
+			t.Errorf("%s - Valid config should not return error, got: %s", tc.desc, d)
+		}
+
+		if tc.wantErr && !d.HasErrors() {
+			t.Errorf("%s - Wrong config should have returned an error", tc.desc)
+		}
+
+		m, err := c.RenderManifests()
+		if err != nil {
+			t.Errorf("%s - Rendering manifests with valid config should succeed, got: %s", tc.desc, err)
+		}
+
+		if len(m) == 0 {
+			t.Errorf("%s - Rendered manifests shouldn't be empty", tc.desc)
+		}
+	}
+}
diff --git a/pkg/components/httpbin/component_test.go b/pkg/components/httpbin/component_test.go
index fa8b69c3..456bcad2 100644
--- a/pkg/components/httpbin/component_test.go
+++ b/pkg/components/httpbin/component_test.go
@@ -40,7 +40,7 @@ component "httpbin" {
 		{
 			desc: "invalid config",
 			hcl: `
-component "dex" {
+component "httpbin" {
   certmanager_cluster_issuer = "letsencrypt-staging"
 }
 			`,