diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 289f17ab..3fc486f2 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -23,4 +23,4 @@ # Please keep the list sorted. Al Cutter - +Roger Ng diff --git a/cmd/example-mysql/main.go b/cmd/example-mysql/main.go new file mode 100644 index 00000000..c517a20d --- /dev/null +++ b/cmd/example-mysql/main.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Tessera authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// example-mysql is a simple personality showing how to use the Tessera MySQL storage implmentation. +package main + +import ( + "crypto/sha256" + "database/sql" + "flag" + "io" + "net/http" + "time" + + tessera "github.com/transparency-dev/trillian-tessera" + "github.com/transparency-dev/trillian-tessera/storage/mysql" + "k8s.io/klog/v2" +) + +var ( + mysqlURI = flag.String("mysql_uri", "user:password@tcp(db:3306)/tessera", "Connection string for a MySQL database") + dbConnMaxLifetime = flag.Duration("db_conn_max_lifetime", 3*time.Minute, "") + dbMaxOpenConns = flag.Int("db_max_open_conns", 64, "") + dbMaxIdleConns = flag.Int("db_max_idle_conns", 64, "") + listen = flag.String("listen", ":2024", "Address:port to listen on") +) + +func main() { + klog.InitFlags(nil) + flag.Parse() + + db, err := sql.Open("mysql", *mysqlURI) + if err != nil { + klog.Exitf("Failed to connect to DB: %v", err) + } + db.SetConnMaxLifetime(*dbConnMaxLifetime) + db.SetMaxOpenConns(*dbMaxOpenConns) + db.SetMaxIdleConns(*dbMaxIdleConns) + + _, err = mysql.New(db) + if err != nil { + klog.Exitf("Failed to create new MySQL storage: %v", err) + } + + http.HandleFunc("POST /add", func(w http.ResponseWriter, r *http.Request) { + b, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + defer r.Body.Close() + + id := sha256.Sum256(b) + _ = tessera.NewEntry(b, tessera.WithIdentity(id[:])) + + // TODO: Add entry to log and return assigned index. + }) + + if err := http.ListenAndServe(*listen, http.DefaultServeMux); err != nil { + klog.Exitf("ListenAndServe: %v", err) + } +} diff --git a/go.mod b/go.mod index 4c8bf3a9..c361d616 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/iam v1.1.8 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -26,6 +27,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.8.1 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/s2a-go v0.1.7 // indirect diff --git a/go.sum b/go.sum index 5460a477..cd53fa81 100644 --- a/go.sum +++ b/go.sum @@ -609,6 +609,8 @@ cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vf cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -702,6 +704,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= diff --git a/storage/mysql/mysql.go b/storage/mysql/mysql.go new file mode 100644 index 00000000..2f321486 --- /dev/null +++ b/storage/mysql/mysql.go @@ -0,0 +1,41 @@ +// Copyright 2024 The Tessera authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mysql contains a MySQL-based storage implementation for Tessera. +package mysql + +import ( + "database/sql" + + _ "github.com/go-sql-driver/mysql" + "k8s.io/klog/v2" +) + +// Storage is a MySQL-based storage implementation for Tessera. +type Storage struct { + db *sql.DB +} + +// New creates a new instance of the MySQL-based Storage. +func New(db *sql.DB) (*Storage, error) { + s := &Storage{ + db: db, + } + if err := s.db.Ping(); err != nil { + klog.Errorf("Failed to ping database: %v", err) + return nil, err + } + + return s, nil +}