Skip to content

Commit

Permalink
Merge pull request #83 from spinup-host/idoqo/gh-80-simplify-startup
Browse files Browse the repository at this point in the history
include spinup UI in setup script, improve CLI with cobra
  • Loading branch information
idoqo authored Dec 13, 2021
2 parents 677401b + cbac5ad commit ea98661
Show file tree
Hide file tree
Showing 12 changed files with 480 additions and 121 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ name: "pre-release"
jobs:
releases-matrix:
runs-on: ubuntu-latest
env:
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
strategy:
matrix:
# build and publish in parallel: linux/386, linux/amd64, windows/386, windows/amd64, darwin/amd64
Expand All @@ -23,5 +25,5 @@ jobs:
goarch: ${{ matrix.goarch }}
goversion: "https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz"
binary_name: "spinup-backend"
ldflags: "-X 'main.apiVersion=v0.2-alpha'"
ldflags: "-X 'main.apiVersion=$RELEASE_TAG_NAME'"
extra_files: README.md CONTRIBUTING.md
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ We are currently using Github Authentication. We should be able to support other
Currently we only support Postgres dbms, but we should be able to support other open source databases like [MySQL](https://www.mysql.com/), [MariaDB](https://mariadb.org/) etc.

![architecture](architecture.jpeg)
### How to run
## Installation
### Linux
- To get started with Spinup on Linux, run the installation script using the command below:
```bash
$ bash < <(curl -s https://raw.githubusercontent.com/spinup-host/spinup/main/scripts/install-spinup.sh)
```
- Add the Spinup installation directory (default is `$HOME/.local/spinup`) to your shell PATH to use Spinup from your terminal.
- Start the Spinup servers (both API and frontend) by running:
```bash
$ spinup start
```

#### Requirement for JWT
We use JWT for verification. You need to have a private and public key that you can create using OpenSSL:
### Others

**To create a private key**
```
Expand Down Expand Up @@ -47,6 +56,9 @@ common:

```go run main.go```

#### Authentication
We use JWT for verification. You need to have a private and public key that you can create using OpenSSL:

On another terminal you can start the [dash](https://github.com/spinup-host/spinup-dash) to access the backend.

To check the API endpoint:
Expand Down
7 changes: 3 additions & 4 deletions api/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"github.com/spinup-host/internal/dockerservice"
"io/ioutil"
"log"
"net"
Expand All @@ -16,11 +17,9 @@ import (
"time"

"github.com/docker/docker/client"
_ "github.com/mattn/go-sqlite3"
"github.com/robfig/cron/v3"
"github.com/spinup-host/backup"
"github.com/spinup-host/internal"

_ "github.com/mattn/go-sqlite3"
"github.com/spinup-host/config"
"github.com/spinup-host/misc"
)
Expand Down Expand Up @@ -215,7 +214,7 @@ func startService(s service, path string) (serviceContainerID string, err error)
return serviceContainerID, err
}

pgExporter := internal.NewPgExporterService(cli, s.DockerNetwork, s.Db.Name, s.Db.Username, s.Db.Password)
pgExporter := dockerservice.NewPgExporterService(cli, s.DockerNetwork, s.Db.Name, s.Db.Username, s.Db.Password)
if err := pgExporter.Start(); err != nil {
return serviceContainerID, err
}
Expand Down
4 changes: 2 additions & 2 deletions backup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package backup

import (
"fmt"
"github.com/spinup-host/internal/dockerservice"
"log"

"github.com/docker/docker/client"
"github.com/spinup-host/internal"
)

func TriggerBackup(networkName, awsAccessKey, awsAccessKeyId, pgHost, pgUsername, pgPassword, walgS3Prefix string) func() {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
fmt.Printf("error creating client %v", err)
}
backupSvc := internal.NewPgBackupService(cli, awsAccessKey, awsAccessKeyId, pgHost, walgS3Prefix, networkName, pgUsername, pgPassword)
backupSvc := dockerservice.NewPgBackupService(cli, awsAccessKey, awsAccessKeyId, pgHost, walgS3Prefix, networkName, pgUsername, pgPassword)
return func() {
fmt.Println("backup triggered")
err = backupSvc.Start()
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ require (
github.com/robfig/cron/v3 v3.0.1
github.com/rs/cors v1.8.0
github.com/rs/zerolog v1.25.0
github.com/spf13/cobra v1.2.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
219 changes: 214 additions & 5 deletions go.sum

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmd

import (
"context"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "spinup",
Short: "Spinup CLI",
TraverseChildren: true,
}

func Execute(ctx context.Context, apiVersion string) error {
rootCmd.AddCommand(versionCmd(apiVersion))
rootCmd.AddCommand(startCmd())

return rootCmd.ExecuteContext(ctx)
}
160 changes: 160 additions & 0 deletions internal/cmd/start.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package cmd

import (
"context"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/spinup-host/api"
"github.com/spinup-host/config"
"github.com/spinup-host/metrics"

"github.com/golang-jwt/jwt"
"github.com/rs/cors"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

var (
cfgFile string
uiPath string

apiPort = ":4434"
uiPort = ":3000"
)

func apiHandler() http.Handler {
rand.Seed(time.Now().UnixNano())
mux := http.NewServeMux()
mux.HandleFunc("/hello", api.Hello)
mux.HandleFunc("/createservice", api.CreateService)
mux.HandleFunc("/githubAuth", api.GithubAuth)
mux.HandleFunc("/logs", api.Logs)
mux.HandleFunc("/jwt", api.JWT)
mux.HandleFunc("/jwtdecode", api.JWTDecode)
mux.HandleFunc("/streamlogs", api.StreamLogs)
mux.HandleFunc("/listcluster", api.ListCluster)
mux.HandleFunc("/cluster", api.GetCluster)
mux.HandleFunc("/metrics", metrics.HandleMetrics)
mux.HandleFunc("/createbackup", api.CreateBackup)
c := cors.New(cors.Options{
AllowedOrigins: []string{"https://app.spinup.host", "http://localhost:3000"},
AllowedHeaders: []string{"authorization", "content-type"},
})

return c.Handler(mux)
}

func uiHandler() http.Handler {
fs := http.FileServer(http.Dir(uiPath))
http.Handle("/", fs)

return http.DefaultServeMux
}

func startCmd() *cobra.Command {
sc := &cobra.Command{
Use: "start",
Short: "start the spinup API and frontend servers",
Run: func(cmd *cobra.Command, args []string) {
home, err := os.UserHomeDir()
if err != nil {
log.Fatalf("FATAL: obtaining home directory: %v", err)
}
cmd.PersistentFlags().StringVar(&cfgFile, "config",
fmt.Sprintf("%s/.local/spinup/config.yaml", home), "Path to spinup configuration")
cmd.PersistentFlags().StringVar(&uiPath, "ui-path",
fmt.Sprintf("%s/.local/spinup/spinup-dash", home), "Path to spinup frontend")

log.Println(fmt.Sprintf("INFO: using config file: %s", cfgFile))
if err = validateConfig(cfgFile); err != nil {
log.Fatalf("FATAL: validating config: %v", err)
}
log.Println("INFO: initial validations successful")

apiListener, err := net.Listen("tcp", apiPort)
if err != nil {
log.Fatalf("FATAL: starting API server %v", err)
}
uiListener, err := net.Listen("tcp", uiPort)
if err != nil {
log.Fatalf("FATAL: starting UI server %v", err)
}
apiServer := &http.Server{
Handler: apiHandler(),
}
uiServer := &http.Server{
Handler: uiHandler(),
}

stopCh := make(chan os.Signal, 1)
go func() {
log.Println(fmt.Sprintf("INFO: starting Spinup API on port %s", apiPort))
apiServer.Serve(apiListener)
}()

go func() {
log.Println(fmt.Sprintf("INFO: starting Spinup UI on port %s", uiPort))
uiServer.Serve(uiListener)
}()

defer stop(apiServer)
defer stop(uiServer)

signal.Notify(stopCh, syscall.SIGINT, syscall.SIGTERM)
log.Println(fmt.Sprint(<-stopCh))
log.Println("stopping spinup apiServer")
},
}

return sc
}

func validateConfig(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()

d := yaml.NewDecoder(file)
if err = d.Decode(&config.Cfg); err != nil {
return err
}

signBytes, err := ioutil.ReadFile(config.Cfg.Common.ProjectDir + "/app.rsa")
if err != nil {
return err
}

if config.Cfg.SignKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes); err != nil {
return err
}

verifyBytes, err := ioutil.ReadFile(config.Cfg.Common.ProjectDir + "/app.rsa.pub")
if err != nil {
return err
}

if config.Cfg.VerifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes); err != nil {
return err
}

return nil
}

func stop(server *http.Server) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) //nolint
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("Can't stop Spinup API correctly: %v", err)
}
}
16 changes: 16 additions & 0 deletions internal/cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cmd

import (
"fmt"
"github.com/spf13/cobra"
)

func versionCmd(version string) *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print the SpinUp version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(fmt.Sprintf("spinup version: %s", version))
},
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package internal
package dockerservice

import (
"context"
Expand Down
Loading

0 comments on commit ea98661

Please sign in to comment.