From a2169f830379779651d7a5d300659d76782453b7 Mon Sep 17 00:00:00 2001 From: Felix Hennig Date: Tue, 1 Feb 2022 10:59:46 +0000 Subject: [PATCH] Druid DB Connection (#71) ## Description This PR refactors the init process and adds a new custom resource for druid connections. The Init resource is now called SupersetDB and also tracks a status. it is first `Provisioned`. If possible (secret also deployed) a kubernetes `Job` is started and the status of the CR becomes `Initializing`. The job is watched, and depending on whether it successfully completes or not, the SupersetDB also becomes `Ready` or `Failed` respectively. This change allows us to also depend on the DB being ready (or not). This is necessary for writing the druid connection information into the DB. A new CRD is introduced: `DruidConnection`. It holds information about the superset DB it should write to, and the Druid cluster that should be connected. A new controller watches the discovery config map as well as the superset DB resources. Once all the dependencies are there (and `Ready`) a job is started, and the status is updated, just like it is done for the `SupersetDB`. This is in line with the declarative philosophy of k8s; the spec reflects the desired state and the status tracks the actual underlying cluster. Also, with multiple controllers watching object states, control flow is delegated a bit to the API server. Before we were watching the job state manually, now we react to status changes which are given to us by the API server. The controllers only react to object changes. This PR also relies on the Druid Discovery PR https://github.com/stackabletech/druid-operator/pull/102 What wasn't done: If the DruidConnection resource is deleted, the actual connection is not deleted. The issue doesn't say that this is necessary, and there is no obvious, documented way to delete data sources from the commandline, we'd have to go into the database and delete stuff there. On the controller side we can use a [finalizer](https://github.com/kube-rs/kube-rs/blob/d90ee9706923ceddf94c3d50928bc390543af2fc/examples/secret_syncer.rs#L92-L102). Co-authored-by: Felix Hennig --- CHANGELOG.md | 6 + Cargo.lock | 33 +-- Cargo.toml | 1 + deploy/crd/druidconnection.crd.yaml | 73 +++++ deploy/crd/supersetcluster.crd.yaml | 15 +- .../{init.crd.yaml => supersetdb.crd.yaml} | 40 ++- deploy/helm/superset-operator/crds/crds.yaml | 130 +++++++-- .../superset-operator/templates/roles.yaml | 28 +- deploy/manifests/crds.yaml | 130 +++++++-- deploy/manifests/roles.yaml | 28 +- .../ROOT/images/superset-databases.png | Bin 0 -> 46205 bytes docs/modules/ROOT/pages/installation.adoc | 3 +- docs/modules/ROOT/pages/usage.adoc | 66 +++-- examples/druidconnection.yaml | 11 + examples/init.command.example.yaml | 9 - examples/simple-superset-cluster.yaml | 3 +- rust/crd/Cargo.toml | 3 +- rust/crd/src/commands.rs | 36 --- rust/crd/src/druidconnection.rs | 88 ++++++ rust/crd/src/lib.rs | 15 +- rust/crd/src/supersetdb.rs | 121 ++++++++ rust/operator-binary/Cargo.toml | 4 +- rust/operator-binary/build.rs | 6 +- .../src/druid_connection_controller.rs | 270 ++++++++++++++++++ rust/operator-binary/src/init_controller.rs | 255 ----------------- rust/operator-binary/src/main.rs | 131 +++++++-- .../src/superset_controller.rs | 61 ++-- .../src/superset_db_controller.rs | 219 ++++++++++++++ rust/operator-binary/src/util.rs | 59 ++++ 29 files changed, 1313 insertions(+), 531 deletions(-) create mode 100644 deploy/crd/druidconnection.crd.yaml rename deploy/crd/{init.crd.yaml => supersetdb.crd.yaml} (55%) create mode 100644 docs/modules/ROOT/images/superset-databases.png create mode 100644 examples/druidconnection.yaml delete mode 100644 examples/init.command.example.yaml delete mode 100644 rust/crd/src/commands.rs create mode 100644 rust/crd/src/druidconnection.rs create mode 100644 rust/crd/src/supersetdb.rs create mode 100644 rust/operator-binary/src/druid_connection_controller.rs delete mode 100644 rust/operator-binary/src/init_controller.rs create mode 100644 rust/operator-binary/src/superset_db_controller.rs create mode 100644 rust/operator-binary/src/util.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 817df4af..a5fcf162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,20 @@ ## [Unreleased] +### Added + +- Druid DB connections can now be configured in a custom resource ([#71]). + ### Changed - Shut down gracefully ([#70]). - All dependencies upgraded. The upgrade to operator-rs 0.8.0 does not force the credentials secret to be set anymore in the custom resource but it is still required ([#82]). +- - `operator-rs` `0.8.0` → `0.9.0` ([#71]) [#70]: https://github.com/stackabletech/superset-operator/pull/70 +[#71]: https://github.com/stackabletech/superset-operator/pull/71 [#82]: https://github.com/stackabletech/superset-operator/pull/82 ## [0.2.0] - 2021-12-17 diff --git a/Cargo.lock b/Cargo.lock index 65694087..accf0418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,9 +137,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.10" +version = "3.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375" +checksum = "08799f92c961c7a1cf0cc398a9073da99e21ce388b46372c37f3191f2f3eed3e" dependencies = [ "atty", "bitflags", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.0.6" +version = "3.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153" +checksum = "0fd2078197a22f338bd4fbf7d6387eb6f0d6a3c69e6cbc09f5c93e97321fd92a" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -854,9 +854,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.113" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" +checksum = "b0005d08a8f7b65fb8073cb697aa0b12b631ed251ce73d862ce50eeb52ce3b50" [[package]] name = "libgit2-sys" @@ -1354,9 +1354,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cf9235533494ea2ddcdb794665461814781c53f19d87b76e571a1c35acbad2b" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] @@ -1373,9 +1373,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dcde03d87d4c973c04be249e7d8f0b35db1c848c487bd43032808e59dd8328d" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -1471,9 +1471,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f82496b90c36d70af5fcd482edaa2e0bd16fade569de1330405fecbbdac736b" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", "winapi", @@ -1481,8 +1481,8 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.8.0" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.8.0#e7ffe8e5804e62ee6c33a0e0d6a85e17f21bf0fd" +version = "0.9.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.9.0#e7c3e2a10c7dd6469c1fdf2fc570e962ea776762" dependencies = [ "async-trait", "backoff", @@ -1519,6 +1519,7 @@ version = "0.3.0-nightly" dependencies = [ "serde", "serde_json", + "snafu", "stackable-operator", "strum", "strum_macros", @@ -1841,9 +1842,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77be66445c4eeebb934a7340f227bfe7b338173d3f8c00a60a5a58005c9faecf" +checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" dependencies = [ "ansi_term", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 3d9de0bf..b02b6ee4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] members = ["rust/crd", "rust/operator-binary"] + diff --git a/deploy/crd/druidconnection.crd.yaml b/deploy/crd/druidconnection.crd.yaml new file mode 100644 index 00000000..eb4f5af1 --- /dev/null +++ b/deploy/crd/druidconnection.crd.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: druidconnections.superset.stackable.tech +spec: + group: superset.stackable.tech + names: + categories: [] + kind: DruidConnection + plural: druidconnections + shortNames: [] + singular: druidconnection + scope: Namespaced + versions: + - additionalPrinterColumns: [] + name: v1alpha1 + schema: + openAPIV3Schema: + description: "Auto-generated derived type for DruidConnectionSpec via `CustomResource`" + properties: + spec: + properties: + druid: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + superset: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + required: + - druid + - superset + type: object + status: + nullable: true + properties: + condition: + enum: + - Pending + - Importing + - Ready + - Failed + type: string + startedAt: + description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. + format: date-time + nullable: true + type: string + required: + - condition + type: object + required: + - spec + title: DruidConnection + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deploy/crd/supersetcluster.crd.yaml b/deploy/crd/supersetcluster.crd.yaml index 49c91086..c2fdc5e3 100644 --- a/deploy/crd/supersetcluster.crd.yaml +++ b/deploy/crd/supersetcluster.crd.yaml @@ -22,6 +22,11 @@ spec: properties: spec: properties: + credentialsSecret: + type: string + loadExamplesOnInit: + nullable: true + type: boolean nodes: nullable: true properties: @@ -32,10 +37,6 @@ spec: type: object config: default: {} - properties: - credentialsSecret: - default: "" - type: string type: object configOverrides: additionalProperties: @@ -59,10 +60,6 @@ spec: type: object config: default: {} - properties: - credentialsSecret: - default: "" - type: string type: object configOverrides: additionalProperties: @@ -125,6 +122,8 @@ spec: description: Desired Superset version nullable: true type: string + required: + - credentialsSecret type: object status: nullable: true diff --git a/deploy/crd/init.crd.yaml b/deploy/crd/supersetdb.crd.yaml similarity index 55% rename from deploy/crd/init.crd.yaml rename to deploy/crd/supersetdb.crd.yaml index ad8f876d..7f1a174c 100644 --- a/deploy/crd/init.crd.yaml +++ b/deploy/crd/supersetdb.crd.yaml @@ -2,61 +2,57 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: inits.command.superset.stackable.tech + name: supersetdbs.superset.stackable.tech spec: - group: command.superset.stackable.tech + group: superset.stackable.tech names: categories: [] - kind: Init - plural: inits + kind: SupersetDB + plural: supersetdbs shortNames: [] - singular: init + singular: supersetdb scope: Namespaced versions: - additionalPrinterColumns: [] name: v1alpha1 schema: openAPIV3Schema: - description: "Auto-generated derived type for InitCommandSpec via `CustomResource`" + description: "Auto-generated derived type for SupersetDBSpec via `CustomResource`" properties: spec: properties: - clusterRef: - description: "A reference to a [`SupersetCluster`]" - properties: - name: - nullable: true - type: string - namespace: - nullable: true - type: string - type: object credentialsSecret: type: string loadExamples: type: boolean + supersetVersion: + type: string required: - - clusterRef - credentialsSecret - loadExamples + - supersetVersion type: object status: nullable: true properties: - finishedAt: - description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. - format: date-time - nullable: true + condition: + enum: + - Pending + - Initializing + - Ready + - Failed type: string startedAt: description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. format: date-time nullable: true type: string + required: + - condition type: object required: - spec - title: Init + title: SupersetDB type: object served: true storage: true diff --git a/deploy/helm/superset-operator/crds/crds.yaml b/deploy/helm/superset-operator/crds/crds.yaml index e941dbf4..54a44b9c 100644 --- a/deploy/helm/superset-operator/crds/crds.yaml +++ b/deploy/helm/superset-operator/crds/crds.yaml @@ -2,63 +2,72 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: inits.command.superset.stackable.tech + name: druidconnections.superset.stackable.tech annotations: helm.sh/resource-policy: keep spec: - group: command.superset.stackable.tech + group: superset.stackable.tech names: categories: [] - kind: Init - plural: inits + kind: DruidConnection + plural: druidconnections shortNames: [] - singular: init + singular: druidconnection scope: Namespaced versions: - additionalPrinterColumns: [] name: v1alpha1 schema: openAPIV3Schema: - description: "Auto-generated derived type for InitCommandSpec via `CustomResource`" + description: "Auto-generated derived type for DruidConnectionSpec via `CustomResource`" properties: spec: properties: - clusterRef: - description: "A reference to a [`SupersetCluster`]" + druid: properties: name: - nullable: true type: string namespace: - nullable: true type: string + required: + - name + - namespace + type: object + superset: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace type: object - credentialsSecret: - type: string - loadExamples: - type: boolean required: - - clusterRef - - credentialsSecret - - loadExamples + - druid + - superset type: object status: nullable: true properties: - finishedAt: - description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. - format: date-time - nullable: true + condition: + enum: + - Pending + - Importing + - Ready + - Failed type: string startedAt: description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. format: date-time nullable: true type: string + required: + - condition type: object required: - spec - title: Init + title: DruidConnection type: object served: true storage: true @@ -90,6 +99,11 @@ spec: properties: spec: properties: + credentialsSecret: + type: string + loadExamplesOnInit: + nullable: true + type: boolean nodes: nullable: true properties: @@ -100,10 +114,6 @@ spec: type: object config: default: {} - properties: - credentialsSecret: - default: "" - type: string type: object configOverrides: additionalProperties: @@ -127,10 +137,6 @@ spec: type: object config: default: {} - properties: - credentialsSecret: - default: "" - type: string type: object configOverrides: additionalProperties: @@ -193,6 +199,8 @@ spec: description: Desired Superset version nullable: true type: string + required: + - credentialsSecret type: object status: nullable: true @@ -205,3 +213,65 @@ spec: storage: true subresources: status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: supersetdbs.superset.stackable.tech + annotations: + helm.sh/resource-policy: keep +spec: + group: superset.stackable.tech + names: + categories: [] + kind: SupersetDB + plural: supersetdbs + shortNames: [] + singular: supersetdb + scope: Namespaced + versions: + - additionalPrinterColumns: [] + name: v1alpha1 + schema: + openAPIV3Schema: + description: "Auto-generated derived type for SupersetDBSpec via `CustomResource`" + properties: + spec: + properties: + credentialsSecret: + type: string + loadExamples: + type: boolean + supersetVersion: + type: string + required: + - credentialsSecret + - loadExamples + - supersetVersion + type: object + status: + nullable: true + properties: + condition: + enum: + - Pending + - Initializing + - Ready + - Failed + type: string + startedAt: + description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. + format: date-time + nullable: true + type: string + required: + - condition + type: object + required: + - spec + title: SupersetDB + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index 1c172704..95b63905 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -19,6 +19,7 @@ rules: - services - endpoints - serviceaccounts + - secrets verbs: - create - delete @@ -68,35 +69,28 @@ rules: verbs: - get - apiGroups: - - command.{{ include "operator.name" . }}.stackable.tech + - {{ include "operator.name" . }}.stackable.tech resources: - - inits - - restarts - - starts - - stops + - {{ include "operator.name" . }}clusters + - druidconnections + - druidconnections/status + - supersetdbs + - supersetdbs/status verbs: - get - list - patch - watch - - apiGroups: - - command.{{ include "operator.name" . }}.stackable.tech - resources: - - inits/status - verbs: - - patch - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: - - {{ include "operator.name" . }}clusters + - {{ include "operator.name" . }}clusters/status verbs: - - get - - list - patch - - watch + # The operator creates the supersetdb for the cluster automatically - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: - - {{ include "operator.name" . }}clusters/status + - supersetdbs verbs: - - patch + - create diff --git a/deploy/manifests/crds.yaml b/deploy/manifests/crds.yaml index 5ad2db91..ef0aa5b7 100644 --- a/deploy/manifests/crds.yaml +++ b/deploy/manifests/crds.yaml @@ -3,63 +3,72 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: inits.command.superset.stackable.tech + name: druidconnections.superset.stackable.tech annotations: helm.sh/resource-policy: keep spec: - group: command.superset.stackable.tech + group: superset.stackable.tech names: categories: [] - kind: Init - plural: inits + kind: DruidConnection + plural: druidconnections shortNames: [] - singular: init + singular: druidconnection scope: Namespaced versions: - additionalPrinterColumns: [] name: v1alpha1 schema: openAPIV3Schema: - description: "Auto-generated derived type for InitCommandSpec via `CustomResource`" + description: "Auto-generated derived type for DruidConnectionSpec via `CustomResource`" properties: spec: properties: - clusterRef: - description: "A reference to a [`SupersetCluster`]" + druid: properties: name: - nullable: true type: string namespace: - nullable: true type: string + required: + - name + - namespace + type: object + superset: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace type: object - credentialsSecret: - type: string - loadExamples: - type: boolean required: - - clusterRef - - credentialsSecret - - loadExamples + - druid + - superset type: object status: nullable: true properties: - finishedAt: - description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. - format: date-time - nullable: true + condition: + enum: + - Pending + - Importing + - Ready + - Failed type: string startedAt: description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. format: date-time nullable: true type: string + required: + - condition type: object required: - spec - title: Init + title: DruidConnection type: object served: true storage: true @@ -91,6 +100,11 @@ spec: properties: spec: properties: + credentialsSecret: + type: string + loadExamplesOnInit: + nullable: true + type: boolean nodes: nullable: true properties: @@ -101,10 +115,6 @@ spec: type: object config: default: {} - properties: - credentialsSecret: - default: "" - type: string type: object configOverrides: additionalProperties: @@ -128,10 +138,6 @@ spec: type: object config: default: {} - properties: - credentialsSecret: - default: "" - type: string type: object configOverrides: additionalProperties: @@ -194,6 +200,8 @@ spec: description: Desired Superset version nullable: true type: string + required: + - credentialsSecret type: object status: nullable: true @@ -206,3 +214,65 @@ spec: storage: true subresources: status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: supersetdbs.superset.stackable.tech + annotations: + helm.sh/resource-policy: keep +spec: + group: superset.stackable.tech + names: + categories: [] + kind: SupersetDB + plural: supersetdbs + shortNames: [] + singular: supersetdb + scope: Namespaced + versions: + - additionalPrinterColumns: [] + name: v1alpha1 + schema: + openAPIV3Schema: + description: "Auto-generated derived type for SupersetDBSpec via `CustomResource`" + properties: + spec: + properties: + credentialsSecret: + type: string + loadExamples: + type: boolean + supersetVersion: + type: string + required: + - credentialsSecret + - loadExamples + - supersetVersion + type: object + status: + nullable: true + properties: + condition: + enum: + - Pending + - Initializing + - Ready + - Failed + type: string + startedAt: + description: Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers. + format: date-time + nullable: true + type: string + required: + - condition + type: object + required: + - spec + title: SupersetDB + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deploy/manifests/roles.yaml b/deploy/manifests/roles.yaml index d52f2814..80a58a41 100644 --- a/deploy/manifests/roles.yaml +++ b/deploy/manifests/roles.yaml @@ -19,6 +19,7 @@ rules: - services - endpoints - serviceaccounts + - secrets verbs: - create - delete @@ -68,35 +69,28 @@ rules: verbs: - get - apiGroups: - - command.superset.stackable.tech + - superset.stackable.tech resources: - - inits - - restarts - - starts - - stops + - supersetclusters + - druidconnections + - druidconnections/status + - supersetdbs + - supersetdbs/status verbs: - get - list - patch - watch - - apiGroups: - - command.superset.stackable.tech - resources: - - inits/status - verbs: - - patch - apiGroups: - superset.stackable.tech resources: - - supersetclusters + - supersetclusters/status verbs: - - get - - list - patch - - watch + # The operator creates the supersetdb for the cluster automatically - apiGroups: - superset.stackable.tech resources: - - supersetclusters/status + - supersetdbs verbs: - - patch + - create diff --git a/docs/modules/ROOT/images/superset-databases.png b/docs/modules/ROOT/images/superset-databases.png new file mode 100644 index 0000000000000000000000000000000000000000..0c67d67bd318165b1254706bf84317534c1f289f GIT binary patch literal 46205 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 Databases: + +image::superset-databases.png[Superset databases showing the connected Druid cluster] \ No newline at end of file diff --git a/examples/druidconnection.yaml b/examples/druidconnection.yaml new file mode 100644 index 00000000..7d95f0f5 --- /dev/null +++ b/examples/druidconnection.yaml @@ -0,0 +1,11 @@ +apiVersion: superset.stackable.tech/v1alpha1 +kind: DruidConnection +metadata: + name: superset-druid-connection +spec: + superset: + name: superset + namespace: default + druid: + name: derby-druid + namespace: default diff --git a/examples/init.command.example.yaml b/examples/init.command.example.yaml deleted file mode 100644 index 419016ea..00000000 --- a/examples/init.command.example.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: command.superset.stackable.tech/v1alpha1 -kind: Init -metadata: - name: superset-cluster-command-init -spec: - clusterRef: - name: superset - credentialsSecret: simple-superset-credentials - loadExamples: true diff --git a/examples/simple-superset-cluster.yaml b/examples/simple-superset-cluster.yaml index 70df5e39..6172ebce 100644 --- a/examples/simple-superset-cluster.yaml +++ b/examples/simple-superset-cluster.yaml @@ -4,8 +4,9 @@ metadata: name: superset spec: version: 1.3.2 + credentialsSecret: simple-superset-credentials + loadExamplesOnInit: true nodes: roleGroups: default: config: - credentialsSecret: simple-superset-credentials diff --git a/rust/crd/Cargo.toml b/rust/crd/Cargo.toml index 4b5ae341..68ff04b4 100644 --- a/rust/crd/Cargo.toml +++ b/rust/crd/Cargo.toml @@ -10,6 +10,7 @@ version = "0.3.0-nightly" [dependencies] serde = "1.0" serde_json = "1.0" -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.8.0" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.9.0" } strum = { version = "0.23", features = ["derive"] } strum_macros = "0.23" +snafu = "0.7" diff --git a/rust/crd/src/commands.rs b/rust/crd/src/commands.rs deleted file mode 100644 index 52f8f3ea..00000000 --- a/rust/crd/src/commands.rs +++ /dev/null @@ -1,36 +0,0 @@ -use serde::{Deserialize, Serialize}; -use stackable_operator::k8s_openapi::apimachinery::pkg::apis::meta::v1::Time; -use stackable_operator::kube::CustomResource; -use stackable_operator::schemars::{self, JsonSchema}; - -use crate::SupersetClusterRef; - -#[derive(Clone, CustomResource, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] -#[kube( - group = "command.superset.stackable.tech", - version = "v1alpha1", - kind = "Init", - plural = "inits", - status = "CommandStatus", - namespaced, - crates( - kube_core = "stackable_operator::kube::core", - k8s_openapi = "stackable_operator::k8s_openapi", - schemars = "stackable_operator::schemars" - ) -)] -#[serde(rename_all = "camelCase")] -pub struct InitCommandSpec { - pub cluster_ref: SupersetClusterRef, - pub credentials_secret: String, - pub load_examples: bool, -} - -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct CommandStatus { - #[serde(skip_serializing_if = "Option::is_none")] - pub started_at: Option