gerrit: auto configure ssh keys

Change-Id: I9c3091e4bb998fa6c846c3c60c2801c7daa45bcf
diff --git a/core/installer/app.go b/core/installer/app.go
index 3656231..64c4078 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -285,6 +285,11 @@
 		}
 	}
 }
+
+#SSHKey: {
+	public: string
+	private: string
+}
 `
 
 type appConfig struct {
diff --git a/core/installer/app_test.go b/core/installer/app_test.go
index 237c806..0dcccda 100644
--- a/core/installer/app_test.go
+++ b/core/installer/app_test.go
@@ -159,6 +159,10 @@
 				"ingressClass": "id-ingress-private",
 				"domain":       "p.bar.ge",
 			},
+			"key": map[string]any{
+				"public":  "foo",
+				"private": "bar",
+			},
 		},
 	}
 	rendered, err := a.Render(d)
diff --git a/core/installer/keys.go b/core/installer/keys.go
index d3dd1d7..3e3d84c 100644
--- a/core/installer/keys.go
+++ b/core/installer/keys.go
@@ -7,3 +7,7 @@
 func NewSSHKeyPair(path string) (*keygen.KeyPair, error) {
 	return keygen.New(path, keygen.WithKeyType(keygen.Ed25519))
 }
+
+func NewECDSASSHKeyPair(path string) (*keygen.KeyPair, error) {
+	return keygen.New(path, keygen.WithKeyType(keygen.ECDSA))
+}
diff --git a/core/installer/repoio.go b/core/installer/repoio.go
index 01a8f91..ffbdcb0 100644
--- a/core/installer/repoio.go
+++ b/core/installer/repoio.go
@@ -395,10 +395,22 @@
 
 func deriveValues(values any, schema Schema, networks []Network) (map[string]any, error) {
 	ret := make(map[string]any)
-	for k, v := range values.(map[string]any) { // TODO(giolekva): validate
-		def, ok := schema.Fields()[k]
+	for k, def := range schema.Fields() {
+		// TODO(gio): validate that it is map
+		v, ok := values.(map[string]any)[k]
+		// TODO(gio): if missing use default value
 		if !ok {
-			return nil, fmt.Errorf("Field not found: %s", k)
+			if def.Kind() == KindSSHKey {
+				key, err := NewECDSASSHKeyPair("tmp")
+				if err != nil {
+					return nil, err
+				}
+				ret[k] = map[string]string{
+					"public":  string(key.RawAuthorizedKey()),
+					"private": string(key.RawPrivateKey()),
+				}
+			}
+			continue
 		}
 		switch def.Kind() {
 		case KindBoolean:
@@ -417,6 +429,12 @@
 				return nil, err
 			}
 			ret[k] = r
+		case KindSSHKey:
+			r, err := deriveValues(v, SSHKeySchema, networks)
+			if err != nil {
+				return nil, err
+			}
+			ret[k] = r
 		case KindStruct:
 			r, err := deriveValues(v, def, networks)
 			if err != nil {
diff --git a/core/installer/schema.go b/core/installer/schema.go
index a692ecf..b96cc13 100644
--- a/core/installer/schema.go
+++ b/core/installer/schema.go
@@ -17,6 +17,7 @@
 	KindStruct       = 2
 	KindNetwork      = 3
 	KindAuth         = 5
+	KindSSHKey       = 6
 	KindNumber       = 4
 )
 
@@ -32,6 +33,13 @@
 	},
 }
 
+var SSHKeySchema Schema = structSchema{
+	fields: map[string]Schema{
+		"public":  basicSchema{KindString},
+		"private": basicSchema{KindString},
+	},
+}
+
 const networkSchema = `
 #Network: {
     name: string
@@ -82,6 +90,30 @@
 	return false
 }
 
+const sshKeySchema = `
+#SSHKey: {
+    public: string
+    private: string
+}
+
+value: { %s }
+`
+
+func isSSHKey(v cue.Value) bool {
+	if v.Value().Kind() != cue.StructKind {
+		return false
+	}
+	s := fmt.Sprintf(sshKeySchema, fmt.Sprintf("%#v", v))
+	c := cuecontext.New()
+	u := c.CompileString(s)
+	sshKey := u.LookupPath(cue.ParsePath("#SSHKey"))
+	vv := u.LookupPath(cue.ParsePath("value"))
+	if err := sshKey.Subsume(vv); err == nil {
+		return true
+	}
+	return false
+}
+
 type basicSchema struct {
 	kind Kind
 }
@@ -119,6 +151,8 @@
 			return basicSchema{KindNetwork}, nil
 		} else if isAuth(v) {
 			return basicSchema{KindAuth}, nil
+		} else if isSSHKey(v) {
+			return basicSchema{KindSSHKey}, nil
 		}
 		s := structSchema{make(map[string]Schema)}
 		f, err := v.Fields(cue.Schema())
diff --git a/core/installer/values-tmpl/gerrit.cue b/core/installer/values-tmpl/gerrit.cue
index 5045e52..be8336b 100644
--- a/core/installer/values-tmpl/gerrit.cue
+++ b/core/installer/values-tmpl/gerrit.cue
@@ -1,6 +1,7 @@
 input: {
 	network: #Network
 	subdomain: string
+	key: #SSHKey
 }
 
 _domain: "\(input.subdomain).\(input.network.domain)"
@@ -175,26 +176,16 @@
 				etc: {
 					secret: {
 						// TODO(gio): auto generate
-						ssh_host_ecdsa_key: ###"""
-						-----BEGIN OPENSSH PRIVATE KEY-----
-						b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
-						1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTLpTYrZ3zFkfRda+q0O3nr119UeN1M
-						H4Ds59cN8NxLpSLZpWn7vLxigN2VCP373Lq5ulUbDojW5qvF2gGppA+4AAAAsHSkAHN0pA
-						BzAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMulNitnfMWR9F1r
-						6rQ7eevXX1R43UwfgOzn1w3w3EulItmlafu8vGKA3ZUI/fvcurm6VRsOiNbmq8XaAamkD7
-						gAAAAhAOzrB8wjiWKzKsrzepkgFbs/CoIT8TBdaPv2aLWPcZr4AAAAFmdlcnJpdEBwLnYw
-						LmRvZG8uY2xvdWQB
-						-----END OPENSSH PRIVATE KEY-----
-						"""###
-						"ssh_host_ecdsa_key.pub": "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMulNitnfMWR9F1r6rQ7eevXX1R43UwfgOzn1w3w3EulItmlafu8vGKA3ZUI/fvcurm6VRsOiNbmq8XaAamkD7g="
+						ssh_host_ecdsa_key: input.key.private
+						"ssh_host_ecdsa_key.pub": input.key.public
 					}
 					config: {
-						"replication.config": ###"""
+						"replication.config": """
 [gerrit]
   autoReload = false
   replicateOnStartup = true
-  defaultForceUpdate = true"""###
-						"gerrit.config": ###"""
+  defaultForceUpdate = true"""
+						"gerrit.config": """
 [gerrit]
   basePath = git # FIXED
   serverId = gerrit-1
@@ -203,17 +194,15 @@
   # LoadBalancer's external IP. This can only be done manually after installing
   # the chart, when you know the external IP the LoadBalancer got from the
   # cluster.
-  canonicalWebUrl = https://gerrit.p.v0.dodo.cloud
+  canonicalWebUrl = https://\(_domain)
   disableReverseDnsLookup = true
 [index]
   type = LUCENE
 [auth]
   type = HTTP
   httpHeader = X-User
-  emailFormat = '{0}@v0.dodo.cloud'
-  # loginUrl = https://accounts-ui.v0.dodo.cloud/
-  # loginText = Sign In with dodo
-  logoutUrl = https://accounts-ui.v0.dodo.cloud/logout
+  emailFormat = '{0}@\(global.domain)'
+  logoutUrl = https://accounts-ui.\(global.domain)/logout
   gitBasicAuthPolicy = HTTP
   userNameToLowerCase = true
   userNameCaseInsensitive = true
@@ -228,7 +217,7 @@
   timeout = 120 s
 [user]
   name = Gerrit Code Review
-  email = gerrit@p.v0.dodo.cloud
+  email = gerrit@\(global.domain)
   anonymousCoward = Unnamed User
 [cache]
   directory = cache
@@ -239,7 +228,7 @@
   javaOptions = -Xms200m
   # Has to be lower than 'gerrit.resources.limits.memory'. Also
   # consider memories used by other applications in the container.
-  javaOptions = -Xmx4g"""###
+  javaOptions = -Xmx4g"""
 					}
 				}
 			}
diff --git a/core/installer/welcome/appmanager-tmpl/app.html b/core/installer/welcome/appmanager-tmpl/app.html
index 088a4c5..09e1f9b 100644
--- a/core/installer/welcome/appmanager-tmpl/app.html
+++ b/core/installer/welcome/appmanager-tmpl/app.html
@@ -42,6 +42,20 @@
         <span>Authentication Groups</span>
       </label>
       <input type="text" name="authGroups" oninput="valueChanged('{{- $name -}}.groups', this.value)" {{ if $readonly }}disabled{{ end }} value="{{ $authGroups }}" />
+	{{ else if eq $schema.Kind 6 }}
+ 	  {{ $sshKey := index $data $name }}
+	  {{ $public := "" }}
+	  {{ $private := "" }}
+	  {{ if $sshKey }}{{ $public = index $sshKey "public" }}{{ end }}
+	  {{ if $sshKey }}{{ $private = index $sshKey "private" }}{{ end }}
+      <label for="{{ $name }}-public">
+        <span>Public Key</span>
+      </label>
+	  <textarea name="{{ $name }}-public" disabled>{{ $public }}</textarea>
+      <label for="{{ $name }}-private">
+        <span>Private Key</span>
+      </label>
+	  <textarea name="{{ $name }}-private" disabled>{{ $private }}</textarea>
     {{ end }}
   {{ end }}
 {{ end }}