diff --git a/.drone.yml b/.drone.yml index 8d0490b3..e3ecec3c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -190,6 +190,23 @@ steps: event: - pull_request +- name: test-libsql + image: rancher/dapper:v0.6.0 + depends_on: + - build + - test-image + commands: + - > + docker run -i -e ARCH -e REPO -e TAG -e DRONE_TAG -e IMAGE_NAME + -v /var/run/docker.sock:/var/run/docker.sock -v kine-cache:/go/src/github.com/k3s-io/kine/.cache + --privileged kine:test-${DRONE_COMMIT} "./scripts/test libsql" + volumes: + - name: docker + path: /var/run/docker.sock + when: + event: + - pull_request + volumes: - name: docker host: diff --git a/go.mod b/go.mod index 505f07fe..ddcefb40 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/shengdoushi/base58 v1.0.0 github.com/sirupsen/logrus v1.9.0 github.com/tidwall/btree v1.6.0 + github.com/tursodatabase/libsql-client-go v0.0.0-20240401075953-8e79a99d828a github.com/urfave/cli v1.22.4 go.etcd.io/etcd/api/v3 v3.5.9 go.etcd.io/etcd/client/pkg/v3 v3.5.9 @@ -30,6 +31,7 @@ require ( ) require ( + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -52,6 +54,7 @@ require ( github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -81,12 +84,12 @@ require ( go.opentelemetry.io/otel/trace v1.0.1 // indirect go.opentelemetry.io/proto/otlp v0.9.0 // indirect go.uber.org/atomic v1.7.0 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.17.0 // indirect golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/net v0.21.0 // indirect - golang.org/x/sync v0.1.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect @@ -96,5 +99,6 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.70.1 // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + nhooyr.io/websocket v1.8.10 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index f8f7c8b1..fe83da28 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -222,6 +224,8 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM= +github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -244,13 +248,9 @@ github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296/go.mod h1:0tqz9Hl github.com/nats-io/jwt/v2 v2.5.5 h1:ROfXb50elFq5c9+1ztaUbdlrArNFl2+fQWP6B8HGEq4= github.com/nats-io/jwt/v2 v2.5.5/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.7.5-0.20220309212130-5c0d1999ff72/go.mod h1:1vZ2Nijh8tcyNe8BDVyTviCd9NYzRbubQYiEHsvOQWc= -github.com/nats-io/nats-server/v2 v2.10.11 h1:yKUiLVincZISpo3A4YljJQ+HfLltGAgoNNJl99KL8I0= -github.com/nats-io/nats-server/v2 v2.10.11/go.mod h1:dXtOqVWzbMTEj+tUyC/itXjJhW37xh0tUBrTAlqAfx8= github.com/nats-io/nats-server/v2 v2.10.12 h1:G6u+RDrHkw4bkwn7I911O5jqys7jJVRY6MwgndyUsnE= github.com/nats-io/nats-server/v2 v2.10.12/go.mod h1:H1n6zXtYLFCgXcf/SF8QNTSIFuS8tyZQMN9NguUHdEs= github.com/nats-io/nats.go v1.13.1-0.20220308171302-2f2f6968e98d/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nats.go v1.33.1 h1:8TxLZZ/seeEfR97qV0/Bl939tpDnt2Z2fK3HkPypj70= -github.com/nats-io/nats.go v1.33.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nats.go v1.34.0 h1:fnxnPCNiwIG5w08rlMcEKTUw4AV/nKyGCOJE8TdhSPk= github.com/nats-io/nats.go v1.34.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= @@ -326,6 +326,8 @@ github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tursodatabase/libsql-client-go v0.0.0-20240401075953-8e79a99d828a h1:LMz5RmEKz1epPZgUO3MtA5/X9Peudqm4rUoteSjLkho= +github.com/tursodatabase/libsql-client-go v0.0.0-20240401075953-8e79a99d828a/go.mod h1:2Fu26tjM011BLeR5+jwTfs6DX/fNMEWV/3CBZvggrA4= github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= @@ -373,8 +375,6 @@ go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSs go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -389,8 +389,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -403,6 +401,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -478,8 +478,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -523,8 +523,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -716,6 +714,8 @@ k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= +nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/drivers/libsql/libsql.go b/pkg/drivers/libsql/libsql.go new file mode 100644 index 00000000..bb618916 --- /dev/null +++ b/pkg/drivers/libsql/libsql.go @@ -0,0 +1,156 @@ +//go:build cgo +// +build cgo + +package libsql + +import ( + "context" + "database/sql" + "net/url" + "os" + "strings" + "time" + + "github.com/k3s-io/kine/pkg/drivers/generic" + "github.com/k3s-io/kine/pkg/logstructured" + "github.com/k3s-io/kine/pkg/logstructured/sqllog" + "github.com/k3s-io/kine/pkg/server" + "github.com/k3s-io/kine/pkg/util" + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" + + // libsql driver + _ "github.com/tursodatabase/libsql-client-go/libsql" +) + +const ( + defaultDSN = "file:./db/state.db&cache=shared&_busy_timeout=30000" +) + +var ( + schema = []string{ + `CREATE TABLE IF NOT EXISTS kine + ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name INTEGER, + created INTEGER, + deleted INTEGER, + create_revision INTEGER, + prev_revision INTEGER, + lease INTEGER, + value BLOB, + old_value BLOB + )`, + `CREATE INDEX IF NOT EXISTS kine_name_index ON kine (name)`, + `CREATE INDEX IF NOT EXISTS kine_name_id_index ON kine (name,id)`, + `CREATE INDEX IF NOT EXISTS kine_id_deleted_index ON kine (id,deleted)`, + `CREATE INDEX IF NOT EXISTS kine_prev_revision_index ON kine (prev_revision)`, + `CREATE UNIQUE INDEX IF NOT EXISTS kine_name_prev_revision_uindex ON kine (name, prev_revision)`, + } +) + +func New(ctx context.Context, dataSourceName string, connPoolConfig generic.ConnectionPoolConfig, metricsRegisterer prometheus.Registerer) (server.Backend, error) { + backend, _, err := NewVariant(ctx, "libsql", dataSourceName, connPoolConfig, metricsRegisterer) + return backend, err +} + +func NewVariant(ctx context.Context, driverName, dataSourceName string, connPoolConfig generic.ConnectionPoolConfig, metricsRegisterer prometheus.Registerer) (server.Backend, *generic.Generic, error) { + if dataSourceName == "" { + if err := os.MkdirAll("./db", 0700); err != nil { + return nil, nil, err + } + } + dataSourceName, err := prepareDSN(dataSourceName) + if err != nil { + return nil, nil, err + } + + dialect, err := generic.Open(ctx, driverName, dataSourceName, connPoolConfig, "?", false, metricsRegisterer) + if err != nil { + return nil, nil, err + } + + dialect.LastInsertID = true + dialect.GetSizeSQL = `SELECT SUM(pgsize) FROM dbstat` + dialect.CompactSQL = ` + DELETE FROM kine AS kv + WHERE + kv.id IN ( + SELECT kp.prev_revision AS id + FROM kine AS kp + WHERE + kp.name != 'compact_rev_key' AND + kp.prev_revision != 0 AND + kp.id <= ? + UNION + SELECT kd.id AS id + FROM kine AS kd + WHERE + kd.deleted != 0 AND + kd.id <= ? + )` + dialect.TranslateErr = func(err error) error { + if strings.Contains(err.Error(), "UNIQUE constraint") { + return server.ErrKeyExists + } + return err + } + dialect.ErrCode = func(err error) string { + if err == nil { + return "" + } + return err.Error() + } + + // this is the first SQL that will be executed on a new DB conn so + // loop on failure here because in the case of dqlite it could still be initializing + for i := 0; i < 300; i++ { + err = setup(dialect.DB) + if err == nil { + break + } + logrus.Errorf("failed to setup db: %v", err) + select { + case <-ctx.Done(): + return nil, nil, ctx.Err() + case <-time.After(time.Second): + } + time.Sleep(time.Second) + } + if err != nil { + return nil, nil, errors.Wrap(err, "setup db") + } + + dialect.Migrate(context.Background()) + return logstructured.New(sqllog.New(dialect)), dialect, nil +} + +func setup(db *sql.DB) error { + logrus.Infof("Configuring database table schema and indexes, this may take a moment...") + + for _, stmt := range schema { + logrus.Tracef("SETUP EXEC : %v", util.Stripped(stmt)) + _, err := db.Exec(stmt) + if err != nil { + return err + } + } + + logrus.Infof("Database tables and indexes are up to date") + return nil +} + +func prepareDSN(dataSourceName string) (string, error) { + if len(dataSourceName) == 0 { + dataSourceName = defaultDSN + } else { + dataSourceName = "libsql://" + dataSourceName + } + _, err := url.Parse(dataSourceName) + if err != nil { + return "", err + } + + return dataSourceName, nil +} diff --git a/pkg/drivers/libsql/libsql_nocgo.go b/pkg/drivers/libsql/libsql_nocgo.go new file mode 100644 index 00000000..77ffd447 --- /dev/null +++ b/pkg/drivers/libsql/libsql_nocgo.go @@ -0,0 +1,28 @@ +//go:build !cgo +// +build !cgo + +package libsql + +import ( + "context" + "database/sql" + "errors" + + "github.com/k3s-io/kine/pkg/drivers/generic" + "github.com/k3s-io/kine/pkg/server" + "github.com/prometheus/client_golang/prometheus" +) + +var errNoCgo = errors.New("this binary is built without CGO, libsql is disabled") + +func New(ctx context.Context, dataSourceName string, connPoolConfig generic.ConnectionPoolConfig, metricsRegisterer prometheus.Registerer) (server.Backend, error) { + return nil, errNoCgo +} + +func NewVariant(driverName, dataSourceName string, connPoolConfig generic.ConnectionPoolConfig, metricsRegisterer prometheus.Registerer) (server.Backend, *generic.Generic, error) { + return nil, nil, errNoCgo +} + +func setup(db *sql.DB) error { + return errNoCgo +} diff --git a/pkg/endpoint/endpoint.go b/pkg/endpoint/endpoint.go index 6e82eb3b..ffc9bbfa 100644 --- a/pkg/endpoint/endpoint.go +++ b/pkg/endpoint/endpoint.go @@ -10,6 +10,7 @@ import ( "github.com/k3s-io/kine/pkg/drivers/dqlite" "github.com/k3s-io/kine/pkg/drivers/generic" + "github.com/k3s-io/kine/pkg/drivers/libsql" "github.com/k3s-io/kine/pkg/drivers/mysql" "github.com/k3s-io/kine/pkg/drivers/nats" "github.com/k3s-io/kine/pkg/drivers/pgsql" @@ -35,6 +36,7 @@ const ( NATSBackend = "nats" MySQLBackend = "mysql" PostgresBackend = "postgres" + LibSQLBackend = "libsql" ) type Config struct { @@ -224,6 +226,8 @@ func getKineStorageBackend(ctx context.Context, driver, dsn string, cfg Config) backend, err = nats.NewLegacy(ctx, dsn, cfg.BackendTLSConfig) case NATSBackend: backend, err = nats.New(ctx, dsn, cfg.BackendTLSConfig) + case LibSQLBackend: + backend, err = libsql.New(ctx, dsn, cfg.ConnectionPoolConfig, cfg.MetricsRegisterer) default: return false, nil, fmt.Errorf("storage backend is not defined") } diff --git a/scripts/test b/scripts/test index 14814399..7f742b98 100755 --- a/scripts/test +++ b/scripts/test @@ -43,4 +43,7 @@ echo "did test-nats-embedded $?" . ./scripts/test-run-nats-socket echo "did test-nats-socket $?" +. ./scripts/test-run-libsql +echo "did test-run-libsql $?" + exit 0 diff --git a/scripts/test-run-libsql b/scripts/test-run-libsql new file mode 100755 index 00000000..9ba1c895 --- /dev/null +++ b/scripts/test-run-libsql @@ -0,0 +1,23 @@ +#!/bin/bash +start-test() { + local ip=$(cat $TEST_DIR/databases/*/metadata/ip) + local image="curlimages/curl:8.7.1" + local req='{"statements": ["CREATE TABLE IF NOT EXISTS users (username)","INSERT INTO users VALUES (\"alice\")"]}' + DB_CONNECTION_TEST=" + echo '$req' | docker run --rm + --name connection-test + $image -d @- + http://$ip:8080" \ + timeout --foreground 1m bash -c "wait-for-db-connection" + KINE_IMAGE=$IMAGE KINE_ENDPOINT="libsql://$ip:8080?tls=0" provision-kine + local kine_url=$(cat $TEST_DIR/kine/*/metadata/url) + K3S_DATASTORE_ENDPOINT=$kine_url provision-cluster +} +export -f start-test + +VERSION_LIST="\ + libsql-server v0.24.4" + +while read ENGINE VERSION; do + LABEL=$ENGINE-$VERSION DB_PASSWORD_ENV=LIBSQL_PASSWORD DB_IMAGE=ghcr.io/tursodatabase/$ENGINE:$VERSION run-test +done <<< $VERSION_LIST