Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Oracle backend support #45

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,17 @@ Kine is an etcdshim that translates etcd API to sqlite, Postgres, Mysql, and dql
- Can be ran standalone so any k8s (not just k3s) can use Kine
- Implements a subset of etcdAPI (not usable at all for general purpose etcd)
- Translates etcdTX calls into the desired API (Create, Update, Delete)
- Backend drivers for dqlite, sqlite, Postgres, MySQL
- Backend drivers for dqlite, sqlite, Postgres, MySQL, Oracle (experimental)

### Using Oracle Backend (experimental)

The Oracle backend is backed by [godror/godror](https://github.com/godror/godror), that uses a C-based OCI wrapper,
so native OCI drivers are still required to start Kine.
However, as those drivers are proprietary and dynamically linked, you will have to manually download it yourself and
point your LD_LIBRARY_PATH to the right direction.

Oracle has outlined the procedure to use the drivers correctly. You can refer to their documentation
[here](https://docs.oracle.com/en/database/oracle/oracle-database/19/lnoci/instant-client.html#GUID-7D65474A-8790-4E81-B535-409010791C2F).

To download the drivers, please go [here](https://www.oracle.com/hk/database/technologies/instant-client/downloads.html)
and carefully read their licensing terms. (unless you want Oracle to send you the papers)
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/Rican7/retry v0.1.0
github.com/canonical/go-dqlite v1.5.1
github.com/go-sql-driver/mysql v1.4.1
github.com/godror/godror v0.17.0
github.com/lib/pq v1.1.1
github.com/mattn/go-sqlite3 v1.10.0
github.com/pkg/errors v0.8.1
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
Expand Down Expand Up @@ -123,6 +125,8 @@ github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85n
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godror/godror v0.17.0 h1:ClVuKG0qCj6f182BruhwgHS8xCgfpQp35pTrefSq4NE=
github.com/godror/godror v0.17.0/go.mod h1:DE94Br7LXn4dQGCexePriVrKotR9GkVzPPT5nnm8dj0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
Expand All @@ -146,6 +150,8 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
Expand Down Expand Up @@ -357,6 +363,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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=
Expand Down Expand Up @@ -401,6 +409,8 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
Expand Down
102 changes: 102 additions & 0 deletions pkg/drivers/oracle/oracle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package oracle

import (
"context"
"database/sql"
"fmt"
"strings"

"github.com/godror/godror"

"github.com/rancher/kine/pkg/drivers/generic"
"github.com/rancher/kine/pkg/logstructured"
"github.com/rancher/kine/pkg/logstructured/sqllog"
"github.com/rancher/kine/pkg/server"
)

const (
defaultHostDSN = "system@localhost"
)

var (
schema = []string{`
CREATE TABLE kine (
id NUMBER GENERATED ALWAYS as IDENTITY,
name VARCHAR(630),
created INTEGER,
deleted INTEGER,
create_revision INTEGER,
prev_revision INTEGER,
lease INTEGER,
value BLOB,
old_value BLOB,
CONSTRAINT kine_pk PRIMARY KEY (id)
)`,
`CREATE INDEX kine_name_index ON kine (name)`,
`CREATE INDEX kine_name_id_index ON kine (name,id)`,
`CREATE UNIQUE INDEX kine_name_prev_revision_uindex ON kine (name, prev_revision)`,
}
procedureTemplate = []string{
"declare",
"begin",
"%s",
"exception when others then",
"if SQLCODE = -955 then null; else raise; end if;",
"end;",
}
)

func New(ctx context.Context, dataSourceName string) (server.Backend, error) {
parsedDSN, err := prepareDSN(dataSourceName)
if err != nil {
return nil, err
}

dialect, err := generic.Open(ctx, "godror", parsedDSN, "?", false)
if err != nil {
return nil, err
}
dialect.LastInsertID = true
dialect.TranslateErr = func(err error) error {
// ORA-00001: unique constraint violated
if err, ok := err.(*godror.OraErr); ok && err.Code() == 1 {
return server.ErrKeyExists
}
return err
}
if err := setup(dialect.DB); err != nil {
return nil, err
}

dialect.Migrate(context.Background())
return logstructured.New(sqllog.New(dialect)), nil
}

func setup(db *sql.DB) error {
var str strings.Builder

for _, cmd := range procedureTemplate {
Copy link
Member

Choose a reason for hiding this comment

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

I am only lightly versed in oracle's SQL dialect, but why is it necessary to wrap all the queries in a templated procedure wrapper? Is that the only way to keep errors from messing up the connection state or something?

if cmd != "%s" {
str.WriteString(cmd + "\n")
} else {
for _, stmt := range schema {
str.WriteString(fmt.Sprintf("execute immediate '%s';\n", stmt))
}
}
}
_, err := db.Exec(str.String())
return err
}

func prepareDSN(dataSourceName string) (string, error) {
if len(dataSourceName) == 0 {
dataSourceName = defaultHostDSN
}
config, err := godror.ParseConnString(dataSourceName)
if err != nil {
return "", err
}

parsedDSN := config.StringWithPassword()
return parsedDSN, nil
}
9 changes: 7 additions & 2 deletions pkg/endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import (
"strings"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"

"github.com/rancher/kine/pkg/drivers/dqlite"
"github.com/rancher/kine/pkg/drivers/mysql"
"github.com/rancher/kine/pkg/drivers/oracle"
"github.com/rancher/kine/pkg/drivers/pgsql"
"github.com/rancher/kine/pkg/drivers/sqlite"
"github.com/rancher/kine/pkg/server"
"github.com/rancher/kine/pkg/tls"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
)

const (
Expand All @@ -25,6 +27,7 @@ const (
ETCDBackend = "etcd3"
MySQLBackend = "mysql"
PostgresBackend = "postgres"
OracleBackend = "oracle"
)

type Config struct {
Expand Down Expand Up @@ -131,6 +134,8 @@ func getKineStorageBackend(ctx context.Context, driver, dsn string, cfg Config)
backend, err = pgsql.New(ctx, dsn, cfg.Config)
case MySQLBackend:
backend, err = mysql.New(ctx, dsn, cfg.Config)
case OracleBackend:
backend, err = oracle.New(ctx, dsn)
default:
return false, nil, fmt.Errorf("storage backend is not defined")
}
Expand Down