Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
bbb8eeb
add delegation verification tool
ghost-not-in-the-shell Oct 25, 2021
c9a0938
returns 0 even if the verification fails
ghost-not-in-the-shell Oct 25, 2021
c5f039c
adopt to new path structure for delegation data
ghost-not-in-the-shell Oct 26, 2021
872c299
disable logs
ghost-not-in-the-shell Oct 26, 2021
2c5e354
adding dockerfiles for stateless verification tool
ghost-not-in-the-shell Oct 27, 2021
6bf8a3c
add delegation-verify service
ghost-not-in-the-shell Oct 27, 2021
e04dda9
remote libp2p&go stuff in docker file
ghost-not-in-the-shell Oct 28, 2021
e5fe672
add readme
ghost-not-in-the-shell Oct 28, 2021
3821572
add delegation-verify as valid service
ghost-not-in-the-shell Oct 28, 2021
f367386
Use base image from URL
georgeee Oct 28, 2021
81da07c
inline verifier
ghost-not-in-the-shell Nov 1, 2021
73bd8d8
inline verifier
ghost-not-in-the-shell Nov 1, 2021
9514c14
adding bisect_ppx
ghost-not-in-the-shell Nov 2, 2021
b600f7a
adding an option "no-checks" to bypass all the checks
ghost-not-in-the-shell Jan 25, 2022
1313513
fix typo
ghost-not-in-the-shell Jan 26, 2022
9da5099
delay the creation of the verifier
ghost-not-in-the-shell Jan 26, 2022
f65a471
wrap verification in a pair of functions
ghost-not-in-the-shell Jan 26, 2022
2bdc2e0
remove rpc_parallel stuff
ghost-not-in-the-shell Jan 26, 2022
5238f49
Fix after rebase onto latest compatible
georgeee Sep 8, 2023
0e8822a
Support graphql_control_port
georgeee Sep 8, 2023
bdc34ea
Add --config-file optional argument
georgeee Sep 10, 2023
ff8477d
[delegation_verify] Refactor in order to abstract over input loading.
Sventimir Nov 9, 2023
68b9281
[delegation_verify] Refactor error handling.
Sventimir Nov 10, 2023
b1cc46b
Add an option to read data from Cassandra (unfinished).
Sventimir Nov 13, 2023
510a61e
[delegation_verify] Refactor to abstract over data source.
Sventimir Nov 14, 2023
4faba0f
[delegation_verify] Load input from Cassandra.
Sventimir Nov 15, 2023
1c5ecdd
Skip empty lines in order to avoid JSON parsing errors.
Sventimir Nov 16, 2023
bcffd44
[delegation_verify] Fix problems with reading Cassandra blobs.
Sventimir Nov 16, 2023
57c846e
[delegation_verify] Add identification data to the output.
Sventimir Nov 16, 2023
ca3bfde
[delegation_verify] Abstract over output mechanism.
Sventimir Nov 16, 2023
6322cb6
[delegation_verify] Upload results to Cassandra.
Sventimir Nov 16, 2023
43f9a9d
[delegation_verify] Use the proper keys for Cassandra records.
Sventimir Nov 17, 2023
31ecc3f
[delegation_verify] Allow for querying entries from multiple days.
Sventimir Nov 17, 2023
eb2ee34
PM-633 Add delegation_verify package and docker image, update libp2p …
smorci Nov 17, 2023
88be126
add delegation verification tool
ghost-not-in-the-shell Oct 25, 2021
b472a26
returns 0 even if the verification fails
ghost-not-in-the-shell Oct 25, 2021
9f50f23
adopt to new path structure for delegation data
ghost-not-in-the-shell Oct 26, 2021
2e94b64
disable logs
ghost-not-in-the-shell Oct 26, 2021
90fb0c5
adding dockerfiles for stateless verification tool
ghost-not-in-the-shell Oct 27, 2021
69c8735
add delegation-verify service
ghost-not-in-the-shell Oct 27, 2021
1dd68de
remote libp2p&go stuff in docker file
ghost-not-in-the-shell Oct 28, 2021
c311632
add readme
ghost-not-in-the-shell Oct 28, 2021
20cbb4e
add delegation-verify as valid service
ghost-not-in-the-shell Oct 28, 2021
ab158c0
Use base image from URL
georgeee Oct 28, 2021
ece2409
inline verifier
ghost-not-in-the-shell Nov 1, 2021
b046f4f
inline verifier
ghost-not-in-the-shell Nov 1, 2021
efddba6
adding bisect_ppx
ghost-not-in-the-shell Nov 2, 2021
9e8aba0
adding an option "no-checks" to bypass all the checks
ghost-not-in-the-shell Jan 25, 2022
f77c184
fix typo
ghost-not-in-the-shell Jan 26, 2022
43d7b7e
delay the creation of the verifier
ghost-not-in-the-shell Jan 26, 2022
4858ef8
wrap verification in a pair of functions
ghost-not-in-the-shell Jan 26, 2022
3d2c34d
remove rpc_parallel stuff
ghost-not-in-the-shell Jan 26, 2022
eb71b7a
Fix after rebase onto latest compatible
georgeee Sep 8, 2023
05c2743
Support graphql_control_port
georgeee Sep 8, 2023
a39e7e7
Add --config-file optional argument
georgeee Sep 10, 2023
d4a72bc
[delegation_verify] Refactor in order to abstract over input loading.
Sventimir Nov 9, 2023
d786cc4
[delegation_verify] Refactor error handling.
Sventimir Nov 10, 2023
167c5a8
Add an option to read data from Cassandra (unfinished).
Sventimir Nov 13, 2023
41c96bb
[delegation_verify] Refactor to abstract over data source.
Sventimir Nov 14, 2023
e257f01
[delegation_verify] Load input from Cassandra.
Sventimir Nov 15, 2023
06f2bf0
Skip empty lines in order to avoid JSON parsing errors.
Sventimir Nov 16, 2023
02f85b1
[delegation_verify] Fix problems with reading Cassandra blobs.
Sventimir Nov 16, 2023
14801ea
[delegation_verify] Add identification data to the output.
Sventimir Nov 16, 2023
0b6c008
[delegation_verify] Abstract over output mechanism.
Sventimir Nov 16, 2023
7b3baab
[delegation_verify] Upload results to Cassandra.
Sventimir Nov 16, 2023
7f5ab05
[delegation_verify] Use the proper keys for Cassandra records.
Sventimir Nov 17, 2023
c5a2586
[delegation_verify] Allow for querying entries from multiple days.
Sventimir Nov 17, 2023
6eaa437
PM-633 Add delegation_verify package and docker image, update libp2p …
smorci Nov 17, 2023
35290aa
Merge branch 'sventimir/stateless-verification-tool' of github.com:Mi…
smorci Nov 20, 2023
eabbca3
PM-633 Add debugging tools
smorci Nov 20, 2023
e452666
[delegation_verify] Don't use core's dates_between function.
Sventimir Nov 20, 2023
aef3a08
[delegation_verify] Make Cassandra keyspace parametric.
Sventimir Nov 20, 2023
e110c2b
[delegtation_verify] Update the README.
Sventimir Nov 20, 2023
4f02ac9
[delegation_verify] Use core's dates_between function again.
Sventimir Nov 20, 2023
d53da2e
PM-633 Remove debugging tools. add env vars and cassandra
smorci Nov 21, 2023
0501c57
PM-633 Add outputs to README
smorci Nov 22, 2023
16f01d2
Remove stateless verifier dockerfile
smorci Nov 24, 2023
7c6ac01
Add coreutils and bash to delegation verify docker image
smorci Nov 24, 2023
a64c1f4
[delegation_verify] Allow configuration through env variables.
Sventimir Nov 27, 2023
52ed567
[delegation_verify] Allow for partial configuration with env vars.
Sventimir Nov 29, 2023
f83f715
Add cqlsh expansion python packages to delegation verifier binary
smorci Nov 30, 2023
74a1b77
Add cqlsh expansion python packages to delegation verifier binary
smorci Nov 30, 2023
ff8a5ad
Merge branch 'sventimir/stateless-verification-tool' of github.com:Mi…
smorci Dec 4, 2023
f05aa03
Merge branch 'berkeley' into sventimir/stateless-verification-tool
Sventimir Dec 5, 2023
5d2ec4e
[delegation_verify] Use different Cassandra field to identify records.
Sventimir Dec 5, 2023
8543f09
[delegation_verify] Make the upper interval bound exclusive.
Sventimir Dec 5, 2023
1c16a94
Add mina delegation verify init derivation
smorci Dec 5, 2023
4521491
Merge branch 'sventimir/stateless-verification-tool' of github.com:Mi…
smorci Dec 5, 2023
f137fb3
Cleanup and formatting nix code
smorci Dec 5, 2023
dcf9cca
Add aws, base image from nix image, add authentication
smorci Dec 14, 2023
cd8dd47
load from s3 if block not found in cassandra
piotr-iohk Dec 15, 2023
8573bc7
applu code review suggestions
piotr-iohk Dec 18, 2023
a94b34e
load_block_from_submission in Cassandra module
piotr-iohk Dec 20, 2023
452717f
On update, purge heavy data (raw_block, snark_work) in Cassandra and …
piotr-iohk Dec 20, 2023
10f63c5
Merge branch 'berkeley' into sventimir/stateless-verification-tool
Sventimir Jan 8, 2024
2a243ed
Add stateless_verifier dockerfile back
piotr-iohk Jan 8, 2024
eb429ea
Copy authentication script to docker image
smorci Jan 9, 2024
06d4a2a
fix entrypoint
piotr-iohk Jan 12, 2024
5d12b04
run cqlsh-expansion.init
piotr-iohk Jan 12, 2024
028fe9e
Multi stage docker delegation verify
smorci Jan 15, 2024
c7affcb
Add C libraries to delegation verify docker image
smorci Jan 15, 2024
8f771a8
Configure localtime in docker image for delegation verify
smorci Jan 15, 2024
36463f7
Remove nix changes - moved to branch smorci/nix-cqlsh-delegation-veri…
smorci Jan 16, 2024
6722c24
Change build instructions on README
smorci Jan 16, 2024
4acffe1
Change HOME variable in delegation verify docker image
smorci Jan 17, 2024
ba3c89a
Add awk and dnsutils to delegation verify docker image
smorci Jan 19, 2024
c82ed1b
fix /bin/aws link
piotr-iohk Jan 23, 2024
9a9bc3d
add pytz
piotr-iohk Jan 23, 2024
fd74436
Merge remote-tracking branch 'origin/berkeley' into sventimir/statele…
piotr-iohk Feb 6, 2024
3d1078f
Merge branch 'berkeley' into sventimir/stateless-verification-tool
ejMina226 Feb 14, 2024
2dc3396
Merge branch 'berkeley' into sventimir/stateless-verification-tool
ejMina226 Feb 15, 2024
fa64d59
Merge branch 'berkeley' into sventimir/stateless-verification-tool
ejMina226 Feb 19, 2024
ec25f6f
Update input type
ejMina226 Feb 19, 2024
9f1ec20
Merge pull request #15145 from MinaProtocol/stateless-update-fs-type
ejMina226 Feb 19, 2024
75b1cab
Reformat
ejMina226 Feb 20, 2024
21f5e5c
Revert changes to nix configuration.
Sventimir Feb 20, 2024
4511e76
Rename a record field for clarity.
Sventimir Feb 20, 2024
e1a16ef
Reformat
ejMina226 Feb 21, 2024
0850740
Revert README
ejMina226 Feb 21, 2024
26a8271
ReadMe
ejMina226 Feb 21, 2024
deed738
readMe
ejMina226 Feb 21, 2024
77953bf
Merge branch 'berkeley' into sventimir/stateless-verification-tool
ejMina226 Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions dockerfiles/Dockerfile-delegation-stateless-verifier
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#################################################################################################
# The "stateless verification build" Stage
# - builds stateless verification tool
# - should not include any data related to joining a specific network, only the node software itself
#################################################################################################
FROM gcr.io/o1labs-192920/mina-toolchain@sha256:c810338e2c3973f7c674d0607048725917ce2be23b949c4bc9760c01122f884b AS builder

# Use --build-arg "DUNE_PROFILE=dev" to build a dev image or for CI
ARG DUNE_PROFILE=devnet

# branch to checkout on first clone (this will be the only availible branch in the container)
# can also be a tagged release
ARG MINA_BRANCH=sventimir/stateless-verification-tool

# repo to checkout the branch from
ARG MINA_REPO=https://github.com/MinaProtocol/mina

# location of repo used for pins and external package commits
ARG MINA_DIR=mina

ENV PATH "$PATH:/usr/lib/go/bin:$HOME/.cargo/bin"

# git will clone into an empty dir, but this also helps us set the workdir in advance
RUN cd $HOME && rm -rf $HOME/${MINA_DIR} \
&& git clone \
-b "${MINA_BRANCH}" \
--depth 1 \
--shallow-submodules \
--recurse-submodules \
${MINA_REPO} ${HOME}/${MINA_DIR}

WORKDIR $HOME/${MINA_DIR}

RUN git submodule sync && git submodule update --init --recursive

RUN mkdir ${HOME}/app

# HACK: build without special cpu features to allow more people to run delegation verification tool
# RUN ./scripts/zexe-standardize.sh

RUN eval $(opam config env) \
&& dune build --profile=${DUNE_PROFILE} \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docker is following different pattern than most of our dockerfiles. Usually we are building debian packages outside the docker and in dockerfile we only download it via apt-get. Pros is that we don't repeat building processes and we don't need to rely on particular toolchain docker as base and we don't need a lot of building mina code above. Cons is that it is tightly coupled with our debian manager. I wonder if writing dockerfile in such way was a decision to break above flow?

Copy link
Contributor Author

@Sventimir Sventimir Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I beleive that's on purpose. Using apt in dockerfiles is messy and we think it's better avoided. Instead we're using Nix to construct the package outside docker and then inject it into the image. Not sure if we even use this dockerfile… @smorci knows the details.

This comment was marked as outdated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sventimir @smorci understand. I think using nix is a future backbone of our CI as rightly pointed out dance with apt-get and docker has performance issues.

However, if we resign with current approach now, there won't be any job in CI which will verify that stateless verification tool build is correct.

I don't want to block merging this PR just because our infrastructure is not ready to handle nix based docker so for now it's ok. It would be nice to have follow up pr for building this docker in CI using nix as POC . We already have a code for verifing nix build:

https://github.com/MinaProtocol/mina/blob/berkeley/buildkite/scripts/test-nix.sh

src/app/delegation_verify/delegation_verify.exe \
&& cp _build/default/src/app/delegation_verify/delegation_verify.exe ./delegation-verify \
&& rm -rf _build

USER root

# copy binary to /bin
RUN cp ./delegation-verify /bin/delegation-verify

# add authenticate.sh to image
RUN cp src/app/delegation_verify/scripts/authenticate.sh /bin/authenticate.sh

# Runtime image
FROM ubuntu:latest

# Copy resources from builder to runtime image
COPY --from=builder /bin/delegation-verify /bin/delegation-verify
COPY --from=builder /bin/authenticate.sh /bin/authenticate.sh

# awscli and cqlsh-expansion are used by the delegation verification tool
RUN apt-get update && apt-get install -y python3 python3-pip jq libjemalloc2 wget dnsutils gawk
RUN pip3 install awscli
RUN pip3 install cqlsh-expansion
RUN pip3 install pytz

# Install libssl1.1.1b (not in apt)
RUN wget https://www.openssl.org/source/openssl-1.1.1b.tar.gz
RUN mkdir /opt/openssl
RUN tar xfvz openssl-1.1.1b.tar.gz --directory /opt/openssl
RUN rm openssl-1.1.1b.tar.gz

ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/openssl/lib"
ENV PATH="$PATH:/opt/openssl/bin"

RUN cd /opt/openssl/openssl-1.1.1b && ./config --prefix=/opt/openssl --openssldir=/opt/openssl/ssl
RUN cd /opt/openssl/openssl-1.1.1b && make && make install

# Rename openssl old binary
RUN mv /usr/bin/openssl /usr/bin/openssl.old

# Install libffi7
RUN wget http://es.archive.ubuntu.com/ubuntu/pool/main/libf/libffi/libffi7_3.3-4_amd64.deb
RUN dpkg -i libffi7_3.3-4_amd64.deb
RUN rm libffi7_3.3-4_amd64.deb

# Make symlinks
RUN ln -s /usr/local/bin/aws /bin/aws
RUN ln -s /usr/local/bin/cqlsh /bin/cqlsh
RUN ln -s /usr/local/bin/cqlsh-expansion /bin/cqlsh-expansion
RUN /usr/local/bin/cqlsh-expansion.init

# make binary and script executable
RUN chmod +x /bin/authenticate.sh /bin/delegation-verify

# set up timezone
ENV DEBIAN_FRONTEND="noninteractive"
ENV TZ="Etc/UTC"
RUN apt install tzdata

# set home to root dir
ENV HOME="/root"

ENTRYPOINT ["/bin/delegation-verify"]
193 changes: 193 additions & 0 deletions src/app/delegation_verify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
Stateless delegation verification
---------------------------------

This is the stateless verification tool that would be used to verify
that data collected by the uptime service. It verifies the snark work
proofs that can optionally be attached to each submission and also it
validates the blocks claimed to be heads of submitters' best chains at
the time of each submission.

The tool can work in two distinct modes:
* **file system mode**, where all submission and blocks should be
stored in files on a local file system;
* **cassandra mode**, where all the data is stored in a Cassandra
database.

Note that submission verification and block validation which this tool
performs closely depends on the version of Mina it was compiled with.
Therefore it is important to always use the verifier tool compiled from
the same revision of the code which produced block and submission one
wants to verify.

File system mode
----------------

Usage:
```
./delegation-verify fs --block-dir <blockdir> <filepath1> <filepath2> ... <filepath{n}>
./delegation-verify fs --block-dir <blockdir> -
```

Where `<filepath{i}>` is a file path of the form
`<basedir>/<submitted_at_date>/<submitted_at>-<submitter>.json`
containing JSON following the format described in
[Cloud storage section of delegation backend spec]
(https://github.com/MinaProtocol/mina/blob/develop/src/app/delegation_backend/README.md#cloud-storage)
(`<basedir>` is a place holder for some directory):

```json
{ "created_at": "server's timestamp (of the time of submission)"
, "remote_addr": "ip:port address from which request has come"
, "peer_id": "peer id (as in user's JSON submission)"
, "snark_work": "(optional) base64-encoded snark work blob"
, "block_hash": "base58check-encoded hash of a block"
, "submitter": "base58check-encoded submitter's public key"
}

```
File path content:
- `submitted_at_date` with server's date (of the time of submission) in format `YYYY-MM-DD`
- `submitted_at` with server's timestamp (of the time of submission) in RFC-3339
- `submitter` is base58check-encoded submitter's public key


Parameter `--block-dir` specifies the path to the directory containing
block for the submission. It is expected that blocks with
`<block_hash>` exist under path `<blockdir>/<block_hash>.dat` for all
of the filepaths provided.

When `-` symbol is used as the `filename1`, no other `filename{i}` are
accepted and all filenames will be read from the `stdin`, one filename
per line.

For each `<filename{i}>` parameter one line of output is printed to
`stdout` :

- Valid payload at `<filename{i}>` :

```json
{ "created_at": "<timestamp of the submission (for identification)>"
, "submitter": "<submitter's public key (for identification)>"
, "parent": "<hash of the parent block>"
, "state_hash": "<state hash of the block in base58 format>"
, "height": "<blockchain height>"
, "slot": "<global slot since genesis corresponding to the block>"
}
```

- Invalid payload at `<filename{i}>` :

```json
{ "error": "<reason of rejection>" }
```

The stateless verification would check the protocol_state_proof in the
block and the transaction_snark_proof in the snark_work.

Cassandra mode
--------------

This mode of operations depends on an external program, `cqlsh` being
installed. It is a Cassandra client written in Python, which
delegation verifier uses to access Cassandra database. This program
should be available in the system path. If it's not, a path to the
`cqlsh` executable can be passed either in `CQLSH` environment
variable or through a command-line option `--clqsh`. Without access
to `cqlsh`, the delegation verifier can only work in the file system
mode described above.

Additionally, the aforementioned Cassandra client requires some
configuration. In particular, an address and credentials to access
the right Cassandra server must be provided. They should be put in a
configuration file: `$HOME/.cassandra/cqlshrc`. The file can look
like this:

```
[authentication]
username = bpu-integration-test-at-673156464838
password = ************************************

[connection]
hostname = cassandra.us-west-2.amazonaws.com
port = 9142
ssl = true

[ssl]
certfile = /home/user/uptime-service-backend/database/cert/sf-class2-root.crt
```

Alternatively this configuration can be provided in environment variables:
* `CASSANDRA_HOST`
* `CASSANDRA_PORT`
* `CASSANDRA_USERNAME`
* `CASSANDRA_PASSWORD`
* `SSL_CERTFILE`
* `CASSANDRA_USE_SSL` – "1", "YES", "yes", "TRUE", "true" all mean yes,
any other value means no.

Usage:
```
./delegation-verify cassandra --keyspace <keyspace-name> <start-timestamp> <end-timstamp>
```

Where:
* `keyspace` is the name of the Cassandra keyspace in the AWS cloud
* `start-timestamp` and `end-timestamp` define a time interval from which
submissions will be taken into account. They should be passed in the
format of the `submitted_at` field above (and in the database), that is:
`YYYY-MM-DD HH:mm:ss.0+0000`, where the trailing zeros describe the
timezone, which should be UTC.

Given this command, the verifier will download submissions from the
given period one by one and verify them. Blocks will also be
downloaded from Cassandra and validated. For each submission, a JSON
statement will be output as shown in the previous
section. Additionally, also appropriate submission records in
Cassandra will be updated with the data. Any errors will also be
logged in Cassandra and **will not** cause the verifier to exit
with a non-zero exit code.

Fallback to AWS S3
------------------

The Cassandra database (in Amazon Keyspaces) has a limitation
of 1MB per row. Consequently, blocks larger than this size will not be
present in the database. To address this, a fallback mechanism is implemented
to retrieve blocks from AWS S3 when they are not found in Cassandra.
This mode relies on `aws` cli tool that needs to be installed on the system.

For this mechanism to function properly, the following environment variables must be set:

* `AWS_CLI` - The path to the AWS CLI tool. If not set, the system defaults to using "/bin/aws".
* `AWS_ACCESS_KEY_ID` - AWS account's access key ID, required for AWS CLI tool to authenticate.
* `AWS_SECRET_ACCESS_KEY` - The secret key paired with access key ID.
* `AWS_S3_BUCKET` - The S3 bucket where submissions and blocks are stored.
* `NETWORK_NAME` - The network name identifier.

The path to access appropriate block data in S3 is constructed based on these variables as follows:

`AWS_S3_BUCKET`/`NETWORK_NAME`/blocks/<block_hash>.dat

Global options
--------------

Some command-line options work with both modes of operation:

* `--no-check` – given this option, the program will skip validation
of blocks and snark work, and will immediately proceed to outputting
state hashes and other metadata for each submission, whether it is
valid or not.

* `--config-file` - a configuration file of the Mina network. It is
necessary to provide it if the network which produced the blocks
used configuration affecting block validation. In most cases it can
be omitted.

Build
-----

To build the docker image go to the root of the repositoryand run

`docker build -t <image-name> -f dockerfiles/Dockerfile-delegation-stateless-verifier .`

where `<image-name>` is your desired image name.
88 changes: 88 additions & 0 deletions src/app/delegation_verify/cassandra.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
open Async
open Core

type 'a parser = Yojson.Safe.t -> 'a Ppx_deriving_yojson_runtime.error_or

type connection_conf = { hostname : string; port : int; use_ssl : bool }

type credentials = { username : string; password : string }

type conf =
{ executable : string
; connection : connection_conf option
; credentials : credentials option
; keyspace : string
}

let make_conn_conf () : connection_conf option =
let open Option.Let_syntax in
let%bind hostname = Sys.getenv "CASSANDRA_HOST" in
let%bind port = Option.map ~f:Int.of_string @@ Sys.getenv "CASSANDRA_PORT" in
let%map use_ssl =
Sys.getenv "CASSANDRA_USE_SSL"
|> Option.map
~f:(List.mem ~equal:String.equal [ "1"; "TRUE"; "true"; "YES"; "yes" ])
in
{ hostname; port; use_ssl }

let make_cred_conf () : credentials option =
let open Option.Let_syntax in
let%bind username = Sys.getenv "CASSANDRA_USERNAME" in
let%map password = Sys.getenv "CASSANDRA_PASSWORD" in
{ username; password }

let make_conf ?executable ~keyspace : conf =
let conn = make_conn_conf () in
let credentials = make_cred_conf () in
let executable =
Option.merge executable (Sys.getenv "CQLSH") ~f:Fn.const
|> Option.value ~default:"cqlsh"
in
{ executable; connection = conn; credentials; keyspace }

let query ~conf q =
let optional ~f = Option.value_map ~f ~default:[] in
let args =
optional conf.credentials ~f:(fun { username; password } ->
[ "--username"; username; "--password"; password ] )
@ optional conf.connection ~f:(fun { hostname; port; use_ssl } ->
(if use_ssl then [ "--ssl" ] else [])
@ [ hostname; Int.to_string port ] )
in
Process.run_lines ~prog:conf.executable ~stdin:q ~args ()

let select ~conf ~parse ~fields ?where from =
let open Deferred.Or_error.Let_syntax in
let%bind data =
query ~conf
@@ Printf.sprintf "SELECT JSON %s FROM %s.%s%s;"
(String.concat ~sep:"," fields)
conf.keyspace from
(match where with None -> "" | Some w -> " WHERE " ^ w)
in
List.slice data 3 (-2) (* skip header and footer *)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree this is not very elegant, I'm not sure what do you suggest here? I prefer this to ignoring lines which don't parse as records. Another idea would be to match against a regular expression to determine which line contains a record and which does not, but then again if we come by erroneous line in the middle of input, we'll ignore it silently, which is not good. In short, I can't really see a better solution which would retain the merit of being very simple and easy to understand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a better suggestion, I'm just sad

|> List.filter ~f:(fun s -> not (String.is_empty s))
|> List.fold_right ~init:(Ok []) ~f:(fun line acc ->
let open Or_error.Let_syntax in
let%bind l = acc in
try
let j = Yojson.Safe.from_string line in
match parse j with
| Ppx_deriving_yojson_runtime.Result.Ok s ->
Ok (s :: l)
| Ppx_deriving_yojson_runtime.Result.Error e ->
Or_error.error_string e
with Yojson.Json_error e -> Or_error.error_string e )
|> Deferred.return

let update ~conf ~table ~where updates =
let open Deferred.Or_error.Let_syntax in
let assignments = List.map updates ~f:(fun (k, v) -> k ^ " = " ^ v) in
let%map _ =
query ~conf
@@ Printf.sprintf "CONSISTENCY LOCAL_QUORUM; UPDATE %s.%s SET %s WHERE %s;"
conf.keyspace table
(String.concat ~sep:"," assignments)
where
in
()
Loading