diff --git a/.gitignore b/.gitignore index ecdfdf7be9d0f55f55e571a8229a175d5ff279cd..17100f70c81e426242461bc6718672e070e147a1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ /vendor /config +# Generated files +assets.bin.go + media-repo*.yaml homeserver.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0e4d40d8953474a227ca16926e81c66fdb2388..bc2c80af98a4bcccc2062d8c0c2394bd2c5a88b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +* Compile assets (templates and migrations) into the binary for ease of deployment. + ### Fixed * Fix error message when an invalid access token is provided. diff --git a/README.md b/README.md index 551482b410e31b60c16c869cdca92f17fb33a456..c7d209dc8f7351b697eb07fb50db31bb2902d067 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,9 @@ for your environment. For help and support, visit [#mediarepo:t2bot.io](https://matrix.to/#/#mediarepo:t2bot.io). -# Installing / building +## Installing / building + +**Note**: developing on matrix-media-repo requires you to follow these steps at least once. Assuming Go 1.12+ is already installed on your PATH: ```bash @@ -46,7 +48,7 @@ docker run --rm -it -p 8000:8000 -v /etc/matrix-media-repo:/data turt2live/matri Note that using `latest` is dangerous - it is effectively the development branch for this project. Instead, prefer to use one of the tagged versions and update regularly. -# Deployment +## Deployment This is intended to run behind a load balancer and beside your homeserver deployments. Assuming your load balancer handles SSL termination, a sample nginx config would be: @@ -97,7 +99,7 @@ listeners: After importing your media, setting `enable_media_repo: false` in your Synapse configuration will disable the media repository. -# Importing media from synapse +## Importing media from synapse Media is imported by connecting to your synapse database and downloading all the content from the homeserver. This is so you have a backup of the media repository still with synapse. **Do not point traffic at the media repo until after the diff --git a/build.ps1 b/build.ps1 index be245b5254a48a10b54e1eef21b7e9fd5cf63a6a..911ffc0a6a5bcf234b0473c56aeb51d689bbab69 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,3 +1,5 @@ $GitCommit = (git rev-list -1 HEAD) $Version = (git describe --tags) +go install -v ./cmd/compile_assets +compile_assets go install -ldflags "-X github.com/turt2live/matrix-media-repo/common/version.GitCommit=$GitCommit -X github.com/turt2live/matrix-media-repo/common/version.Version=$Version" -v ./cmd/... diff --git a/build.sh b/build.sh index 15d83fd786a2882befd728f9e7c81fe25d50ca5c..bfb787f4497be094427426d3f4e2cf4336648b39 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,5 @@ #!/bin/sh +GOBIN=$PWD/bin go install -v ./cmd/compile_assets +compile_assets GOBIN=$PWD/bin go install -ldflags "-X github.com/turt2live/matrix-media-repo/common/version.GitCommit=$(git rev-list -1 HEAD) -X github.com/turt2live/matrix-media-repo/common/version.Version=$(git describe --tags)" -v ./cmd/... diff --git a/cmd/compile_assets/main.go b/cmd/compile_assets/main.go new file mode 100644 index 0000000000000000000000000000000000000000..bd654b0f745c23937e6fe2acd8b81100d20431a5 --- /dev/null +++ b/cmd/compile_assets/main.go @@ -0,0 +1,78 @@ +package main + +import ( + "bytes" + "compress/gzip" + "encoding/hex" + "flag" + "fmt" + "io/ioutil" + "path" + + "github.com/turt2live/matrix-media-repo/common/config" +) + +func main() { + migrationsPath := flag.String("migrations", config.DefaultMigrationsPath, "The absolute path for the migrations folder") + templatesPath := flag.String("templates", config.DefaultTemplatesPath, "The absolute path for the templates folder") + outputFile := flag.String("output", "./common/assets/assets.bin.go", "The output Go file to dump the files into") + flag.Parse() + + fmt.Println("Reading migrations into memory...") + migrations := readDir(*migrationsPath, "migrations") + templates := readDir(*templatesPath, "templates") + + fileMap := make(map[string][]byte) + for k, v := range migrations { + fileMap[k] = v + } + for k, v := range templates { + fileMap[k] = v + } + + fmt.Println("Writing assets go file") + str := "package " + path.Base(path.Dir(*outputFile)) + "\n\n" + str += "// !! THIS FILE IS AUTOMATICALLY GENERATED. You can edit it, but it will be overwritten over time.\n\n" + str += "var compressedFiles = map[string]string{\n" + for f, b := range fileMap { + b64 := hex.EncodeToString(b) + str += fmt.Sprintf("\t\"%s\": \"%s\",\n", f, b64) + } + str += "}\n" + err := ioutil.WriteFile(*outputFile, []byte(str), 644) + if err != nil { + panic(err) + } + + fmt.Println("Done") +} + +func readDir(dir string, pathName string) map[string][]byte { + fileMap := make(map[string][]byte) + files, err := ioutil.ReadDir(dir) + if err != nil { + panic(err) + } + for _, f := range files { + fname := path.Join(dir, f.Name()) + fmt.Println("Reading ", fname) + + b, err := ioutil.ReadFile(fname) + if err != nil { + panic(err) + } + + // Compress the file + fmt.Println("Compressing ", fname) + out := &bytes.Buffer{} + gw, err := gzip.NewWriterLevel(out, gzip.BestCompression) + if err != nil { + panic(err) + } + gw.Write(b) + gw.Close() + + fileMap[path.Join(pathName, f.Name())] = out.Bytes() + } + return fileMap +} diff --git a/cmd/import_synapse/main.go b/cmd/import_synapse/main.go index 651640cce6ddb80790a8e3b6a6ccad5582bd70a2..b1f6b77d83fb44047b7eef621796371d3b65e2f4 100644 --- a/cmd/import_synapse/main.go +++ b/cmd/import_synapse/main.go @@ -14,6 +14,7 @@ import ( "github.com/jeffail/tunny" "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/common" + "github.com/turt2live/matrix-media-repo/common/assets" "github.com/turt2live/matrix-media-repo/common/config" "github.com/turt2live/matrix-media-repo/common/logging" "github.com/turt2live/matrix-media-repo/common/rcontext" @@ -43,7 +44,7 @@ func main() { flag.Parse() config.Path = *configPath - config.Runtime.MigrationsPath = *migrationsPath + assets.SetupTemplatesAndMigrations(*migrationsPath, "") var realPsqlPassword string if *postgresPassword == "" { @@ -115,6 +116,9 @@ func main() { time.Sleep(1 * time.Second) } + // Clean up + assets.Cleanup() + logrus.Info("Import completed") } diff --git a/cmd/media_repo/main.go b/cmd/media_repo/main.go index 43207c35ceaed7ec84926e230e8acd0860386461..d26fc9b62c473c8bdb468e4d5e01d9a540d9c9b7 100644 --- a/cmd/media_repo/main.go +++ b/cmd/media_repo/main.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/api/webserver" + "github.com/turt2live/matrix-media-repo/common/assets" "github.com/turt2live/matrix-media-repo/common/config" "github.com/turt2live/matrix-media-repo/common/logging" "github.com/turt2live/matrix-media-repo/common/runtime" @@ -17,8 +18,8 @@ import ( func main() { configPath := flag.String("config", "media-repo.yaml", "The path to the configuration") - migrationsPath := flag.String("migrations", "./migrations", "The absolute path for the migrations folder") - templatesPath := flag.String("templates", "./templates", "The absolute path for the templates folder") + migrationsPath := flag.String("migrations", config.DefaultMigrationsPath, "The absolute path for the migrations folder") + templatesPath := flag.String("templates", config.DefaultTemplatesPath, "The absolute path for the templates folder") versionFlag := flag.Bool("version", false, "Prints the version and exits") flag.Parse() @@ -34,8 +35,7 @@ func main() { } config.Path = *configPath - config.Runtime.MigrationsPath = *migrationsPath - config.Runtime.TemplatesPath = *templatesPath + assets.SetupTemplatesAndMigrations(*migrationsPath, *templatesPath) err := logging.Setup(config.Get().General.LogDirectory) if err != nil { @@ -99,6 +99,9 @@ func main() { stopAllButWeb() } + // Clean up + assets.Cleanup() + // For debugging logrus.Info("Goodbye!") } diff --git a/common/assets/process.go b/common/assets/process.go new file mode 100644 index 0000000000000000000000000000000000000000..93a473f724043ee7c39c3e937c0b138b52a90296 --- /dev/null +++ b/common/assets/process.go @@ -0,0 +1,79 @@ +package assets + +import ( + "encoding/hex" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + "github.com/turt2live/matrix-media-repo/common/config" +) + +var tempMigrations string +var tempTemplates string + +func SetupTemplatesAndMigrations(givenMigrationsPath string, givenTemplatesPath string) { + _, err := os.Stat(givenMigrationsPath) + exists := err == nil || !os.IsNotExist(err) + if !exists { + tempMigrations, err = ioutil.TempDir(os.TempDir(), "media-repo-migrations") + if err != nil { + panic(err) + } + logrus.Info("Migrations path doesn't exist - attempting to unpack from compiled data") + extractPrefixTo("migrations", tempMigrations) + givenMigrationsPath = tempMigrations + } + + if givenTemplatesPath != "" { + _, err = os.Stat(givenTemplatesPath) + exists = err == nil || !os.IsNotExist(err) + if !exists { + tempTemplates, err = ioutil.TempDir(os.TempDir(), "media-repo-templates") + if err != nil { + panic(err) + } + logrus.Info("Templates path doesn't exist - attempting to unpack from compiled data") + extractPrefixTo("templates", tempTemplates) + givenTemplatesPath = tempTemplates + } + } + + config.Runtime.MigrationsPath = givenMigrationsPath + config.Runtime.TemplatesPath = givenTemplatesPath +} + +func Cleanup() { + if tempMigrations != "" { + logrus.Info("Cleaning up temporary assets directory: ", tempMigrations) + os.Remove(tempMigrations) + } + if tempTemplates != "" { + logrus.Info("Cleaning up temporary assets directory: ", tempTemplates) + os.Remove(tempTemplates) + } +} + +func extractPrefixTo(pathName string, destination string) { + for f, h := range compressedFiles { + if !strings.HasPrefix(f, pathName) { + continue + } + + logrus.Infof("Decoding %s", f) + b, err := hex.DecodeString(h) + if err != nil { + panic(err) + } + + dest := path.Join(destination, filepath.Base(f)) + logrus.Infof("Writing %s to %s", f, dest) + err = ioutil.WriteFile(dest, b, 644) + if err != nil { + panic(err) + } + } +} diff --git a/common/config/access.go b/common/config/access.go index 7f28be2c74e557dbde8877a0c1421a4f9f812b59..69b6cbf26554155afaa465b5d92d24b4771959a1 100644 --- a/common/config/access.go +++ b/common/config/access.go @@ -17,6 +17,9 @@ type runtimeConfig struct { TemplatesPath string } +const DefaultMigrationsPath = "./migrations" +const DefaultTemplatesPath = "./templates" + var Runtime = &runtimeConfig{} var Path = "media-repo.yaml"