cluster/kube: start implementing rook
diff --git a/cluster/kube/cluster.jsonnet b/cluster/kube/cluster.jsonnet
index 2f6da45..5275f66 100644
--- a/cluster/kube/cluster.jsonnet
+++ b/cluster/kube/cluster.jsonnet
@@ -6,6 +6,7 @@
 local calico = import "lib/calico.libsonnet";
 local metallb = import "lib/metallb.libsonnet";
 local nginx = import "lib/nginx.libsonnet";
+local rook = import "lib/rook.libsonnet";
 
 local Cluster(fqdn) = {
     local cluster = self,
@@ -60,6 +61,8 @@
     },
     // Main nginx Ingress Controller
     nginx: nginx.Environment {},
+    // Rook Ceph storage
+    rook: rook.Environment {},
 };
 
 
diff --git a/cluster/kube/lib/rook.libsonnet b/cluster/kube/lib/rook.libsonnet
new file mode 100644
index 0000000..7327500
--- /dev/null
+++ b/cluster/kube/lib/rook.libsonnet
@@ -0,0 +1,147 @@
+# Deploy Rook/Ceph Operator
+
+local kube = import "../../../kube/kube.libsonnet";
+
+{
+    Environment: {
+        local env = self,
+        local cfg = env.cfg,
+        cfg:: {
+            image: "rook/ceph:master",
+            namespace: "rook-ceph-system",
+        },
+
+        metadata:: {
+            namespace: cfg.namespace,
+            labels: {
+                "operator": "rook",
+                "storage-backend": "ceph",
+            },
+        },
+
+        namespace: kube.Namespace(cfg.namespace),
+
+        crds: {
+            cephclusters: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephCluster") {
+                spec+: {
+                    additionalPrinterColumns: [
+                        { name: "DataDirHostPath", type: "string", description: "Directory used on the K8s nodes", JSONPath: ".spec.dataDirHostPath" },
+                        { name: "MonCount", type: "string", description: "Number of MONs", JSONPath: ".spec.mon.count" },
+                        { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
+                        { name: "State", type: "string", description: "Current State", JSONPath: ".status.state" },
+                    ],
+                    validation: {
+                        # Converted from official operator YAML
+                        "openAPIV3Schema": {
+                            "properties": {
+                                "spec": {
+                                    "properties": {
+                                        "cephVersion": {
+                                            "properties": {
+                                                "allowUnsupported": {
+                                                    "type": "boolean"
+                                                },
+                                                "image": {
+                                                    "type": "string"
+                                                },
+                                                "name": {
+                                                    "pattern": "^(luminous|mimic|nautilus)$",
+                                                    "type": "string"
+                                                }
+                                            }
+                                        },
+                                        "dashboard": {
+                                            "properties": {
+                                                "enabled": {
+                                                    "type": "boolean"
+                                                },
+                                                "urlPrefix": {
+                                                    "type": "string"
+                                                },
+                                                "port": {
+                                                    "type": "integer"
+                                                }
+                                            }
+                                        },
+                                        "dataDirHostPath": {
+                                            "pattern": "^/(\\S+)",
+                                            "type": "string"
+                                        },
+                                        "mon": {
+                                            "properties": {
+                                                "allowMultiplePerNode": {
+                                                    "type": "boolean"
+                                                },
+                                                "count": {
+                                                    "maximum": 9,
+                                                    "minimum": 1,
+                                                    "type": "integer"
+                                                },
+                                                "preferredCount": {
+                                                    "maximum": 9,
+                                                    "minimum": 0,
+                                                    "type": "integer"
+                                                }
+                                            },
+                                            "required": [
+                                                "count"
+                                            ]
+                                        },
+                                        "network": {
+                                            "properties": {
+                                                "hostNetwork": {
+                                                    "type": "boolean"
+                                                }
+                                            }
+                                        },
+                                        "storage": {
+                                            "properties": {
+                                                "nodes": {
+                                                    "items": {},
+                                                    "type": "array"
+                                                },
+                                                "useAllDevices": {},
+                                                "useAllNodes": {
+                                                    "type": "boolean"
+                                                }
+                                            }
+                                        }
+                                    },
+                                    "required": [
+                                        "mon"
+                                    ]
+                                }
+                            }
+                        }
+                    }
+                },
+            },
+            cephfilesystems: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephFilesystem") {
+                spec+: {
+                    additionalPrinterColumns: [
+                        { name: "MdsCount", type: "string", description: "Number of MDs", JSONPath: ".spec.metadataServer.activeCount" },
+                        { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
+                    ],
+                },
+            },
+            cephnfses: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephNFS") {
+                spec+: {
+                    names+: {
+                        shortNames: ["nfs"],
+                    },
+                },
+            },
+            cephobjectstores: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephObjectStore"),
+            cephobjectstoreusers: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephObjectStoreUser"),
+            cephblockpools: kube.CustomResourceDefinition("ceph.rook.io", "v1", "CephBlockPool"),
+            volumes: kube.CustomResourceDefinition("rook.io", "v1alpha2", "Volume") {
+                spec+: {
+                    names+: {
+                        shortNames: ["rv"],
+                    },
+                },
+            },
+        },
+
+    },
+}