cloud/{apigw,lib/component}: add cockroachdb client, sample schema

This sets up some boilerplate to connect to CockroachDB servers,
including test in-memory servers.

We also add a first pass apigw user table schema, as the first user of
this new functionality. We exercise that, in turn, in a test.

We also rename component.Configuration to component.ComponentConfig.
There's a stutter in there, but it makes sense with
component.CockroachConfig alongside.

Change-Id: I76691146b87ce135d60db179b3f51eee16525df7
Reviewed-on: https://review.monogon.dev/c/monogon/+/912
Reviewed-by: Leopold Schabel <leo@monogon.tech>
Vouch-Run-CI: Leopold Schabel <leo@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/apigw/model/BUILD.bazel b/cloud/apigw/model/BUILD.bazel
new file mode 100644
index 0000000..081d488
--- /dev/null
+++ b/cloud/apigw/model/BUILD.bazel
@@ -0,0 +1,28 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//build/sqlc:sqlc.bzl", "sqlc_go_library")
+
+sqlc_go_library(
+    name = "sqlc_model",
+    dialect = "cockroachdb",
+    importpath = "source.monogon.dev/cloud/apigw/model",
+    migrations = glob(["migrations/*sql"]),
+    queries = [
+        "queries.sql",
+    ],
+)
+
+go_library(
+    name = "model",
+    srcs = ["migrations.go"],
+    embed = [
+        ":sqlc_model",  # keep
+    ],
+    embedsrcs = glob(["migrations/*sql"]),
+    importpath = "source.monogon.dev/cloud/apigw/model",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@com_github_golang_migrate_migrate_v4//source",
+        "@com_github_golang_migrate_migrate_v4//source/iofs",
+        "@com_github_google_uuid//:uuid",  # keep
+    ],
+)
diff --git a/cloud/apigw/model/migrations.go b/cloud/apigw/model/migrations.go
new file mode 100644
index 0000000..2c07768
--- /dev/null
+++ b/cloud/apigw/model/migrations.go
@@ -0,0 +1,15 @@
+package model
+
+import (
+	"embed"
+
+	"github.com/golang-migrate/migrate/v4/source"
+	"github.com/golang-migrate/migrate/v4/source/iofs"
+)
+
+//go:embed migrations/*.sql
+var migrationData embed.FS
+
+func MigrationsSource() (source.Driver, error) {
+	return iofs.New(migrationData, "migrations")
+}
diff --git a/cloud/apigw/model/migrations/1663155947_initial.down.sql b/cloud/apigw/model/migrations/1663155947_initial.down.sql
new file mode 100644
index 0000000..032d6cf
--- /dev/null
+++ b/cloud/apigw/model/migrations/1663155947_initial.down.sql
@@ -0,0 +1 @@
+DROP TABLE accounts;
\ No newline at end of file
diff --git a/cloud/apigw/model/migrations/1663155947_initial.up.sql b/cloud/apigw/model/migrations/1663155947_initial.up.sql
new file mode 100644
index 0000000..4812e00
--- /dev/null
+++ b/cloud/apigw/model/migrations/1663155947_initial.up.sql
@@ -0,0 +1,17 @@
+CREATE TABLE accounts (
+    -- Internal account ID. Never changes.
+    account_id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
+
+    -- Identity used to tied this account to OIDC.
+    -- OpenID Connect Core, 2. ID Token: “It MUST NOT exceed 255 ASCII
+    -- characters in length”.
+    account_oidc_sub STRING(255) NOT NULL UNIQUE,
+
+    --- Copy/cache of user data retrieved from OIDC IdP on login. Currently this
+    --- is only updated on first login, but we should find a way to trigger
+    --- a re-retrieval.
+    -- Display name preferred by user.
+    -- Self-limiting ourselves to 255 unicode codepoints here. This is also
+    -- supposedly what keycloak also defaults to for user attributes.
+    account_display_name STRING(255) NOT NULL
+);
\ No newline at end of file
diff --git a/cloud/apigw/model/queries.sql b/cloud/apigw/model/queries.sql
new file mode 100644
index 0000000..564f91d
--- /dev/null
+++ b/cloud/apigw/model/queries.sql
@@ -0,0 +1,13 @@
+-- name: GetAccountByOIDC :many
+SELECT
+    accounts.*
+FROM accounts
+WHERE account_oidc_sub = $1;
+
+-- name: InitializeAccountFromOIDC :one
+INSERT INTO accounts (
+    account_oidc_sub, account_display_name
+) VALUES (
+    $1, $2
+)
+RETURNING *;
\ No newline at end of file