Skip to content

Commit

Permalink
Merge pull request #22 from sboy99/feat/cloud
Browse files Browse the repository at this point in the history
feat: backup directly to the cloud
  • Loading branch information
sboy99 authored Jan 16, 2025
2 parents bbbaea8 + 16b3b4e commit cd6e4aa
Show file tree
Hide file tree
Showing 13 changed files with 386 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ generated/
# Config
config.json
config.yml

# Secrets
.env
46 changes: 42 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,23 @@ type database struct {

type StorageEnum string
type storage struct {
Type StorageEnum
Dest string
Type StorageEnum
Dest string //TODO: Move Dest to LocalStorage struct
Cloud cloudStorage
}

type CloudEnum string
type cloudStorage struct {
Type CloudEnum
AWS _AWSCloudStorage
}

type _AWSCloudStorage struct {
Region string
BucketName string
AccessKeyId string
AccessKeySecret string
Endpoint string
}

const (
Expand All @@ -40,8 +55,13 @@ const (
)

const (
LOCAL StorageEnum = "local"
CLOUD StorageEnum = "cloud"
LOCAL StorageEnum = "LOCAL"
CLOUD StorageEnum = "CLOUD"
)

const (
GCP CloudEnum = "GCP"
AWS CloudEnum = "AWS"
)

func init() {
Expand Down Expand Up @@ -75,6 +95,14 @@ func Save(config *Config) error {
// Storage //
viper.Set("storage.type", config.Storage.Type)
viper.Set("storage.dest", config.Storage.Dest)
// Cloud Storage //
viper.Set("storage.cloud.type", config.Storage.Cloud.Type)
// AWS Cloud Storage //
viper.Set("storage.cloud.aws.region", config.Storage.Cloud.AWS.Region)
viper.Set("storage.cloud.aws.bucket_name", config.Storage.Cloud.AWS.BucketName)
viper.Set("storage.cloud.aws.access_key_id", config.Storage.Cloud.AWS.AccessKeyId)
viper.Set("storage.cloud.aws.access_key_secret", config.Storage.Cloud.AWS.AccessKeySecret)
viper.Set("storage.cloud.aws.endpoint", config.Storage.Cloud.AWS.Endpoint)

if err := viper.WriteConfig(); err != nil {
return err
Expand All @@ -99,6 +127,16 @@ func GetConfig() *Config {
Storage: storage{
Type: StorageEnum(viper.GetString("storage.type")),
Dest: viper.GetString("storage.dest"),
Cloud: cloudStorage{
Type: CloudEnum(viper.GetString("storage.cloud.type")),
AWS: _AWSCloudStorage{
Region: viper.GetString("storage.cloud.aws.region"),
BucketName: viper.GetString("storage.cloud.aws.bucket_name"),
AccessKeyId: viper.GetString("storage.cloud.aws.access_key_id"),
AccessKeySecret: viper.GetString("storage.cloud.aws.access_key_secret"),
Endpoint: viper.GetString("storage.cloud.aws.endpoint"),
},
},
},
}
}
7 changes: 7 additions & 0 deletions config/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ import (
)

func setDefaults() {
// App
viper.SetDefault("app.name", "go-vault")
viper.SetDefault("app.version", "0.0.1")

// Storage
viper.SetDefault("storage.type", "local")
viper.SetDefault("storage.dest", "./generated")

// Storage Cloud
// AWS
viper.SetDefault("storage.cloud.aws.endpoint", "default")

}

func validateConfig() error {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ require (
)

require (
github.com/aws/aws-sdk-go v1.55.5 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
Expand All @@ -21,6 +23,9 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down Expand Up @@ -85,6 +90,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
12 changes: 7 additions & 5 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"github.com/sboy99/go-vault/internal/config"
"github.com/sboy99/go-vault/internal/database"
"github.com/sboy99/go-vault/pkg/logger"
"github.com/spf13/cobra"
)

Expand All @@ -16,11 +17,6 @@ var rootCmd = &cobra.Command{
},
}

// Execute runs the root command
func Execute() error {
return rootCmd.Execute()
}

var setupCmd = &cobra.Command{
Use: "setup",
Short: "Setup config of your database.",
Expand All @@ -35,6 +31,7 @@ var backupCmd = &cobra.Command{
Short: "Create a backup of your database.",
Run: func(cmd *cobra.Command, args []string) {
dbService := database.NewDatabaseService()
logger.Info("%v", dbService)
dbService.Backup()
},
}
Expand All @@ -43,3 +40,8 @@ func init() {
rootCmd.AddCommand(setupCmd)
rootCmd.AddCommand(backupCmd)
}

// Execute runs the root command
func Execute() error {
return rootCmd.Execute()
}
69 changes: 60 additions & 9 deletions internal/config/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,55 +12,106 @@ func NewConfigService() *ConfigService {
return &ConfigService{}
}

// TODO: Return error
func (c *ConfigService) SetupConfig() {
// Config //
cfg := config.GetConfig()

// Promt input //
dbType, err := ui.DisplaySelectDatabaseTypePrompt()
if err != nil {
logger.Error("Failed to display select db prompt\nDetails: %v", err)
return
}
cfg.DB.Type = dbType

dbHost, err := ui.DisplayInputDatabaseHostPrompt()
if err != nil {
logger.Error("Failed to display input db host prompt\nDetails: %v", err)
return
}
cfg.DB.Host = dbHost

dbPort, err := ui.DisplayInputDatabasePortPrompt(dbType)
if err != nil {
logger.Error("Failed to display input db port prompt\nDetails: %v", err)
return
}
cfg.DB.Port = dbPort

dbName, err := ui.DisplayInputDatabaseNamePrompt(dbType)
if err != nil {
logger.Error("Failed to display input db name prompt\nDetails: %v", err)
return
}
cfg.DB.Name = dbName

dbUser, err := ui.DisplayInputDatabaseUsernamePrompt(dbType)
if err != nil {
logger.Error("Failed to display input db username prompt\nDetails: %v", err)
return
}
cfg.DB.Username = dbUser

dbPass, err := ui.DisplayInputDatabasePasswordPrompt()
if err != nil {
logger.Error("Failed to display input db password prompt\nDetails: %v", err)
return
}
cfg.DB.Password = dbPass

storageType, err := ui.DisplaySelectStorageTypePrompt()
if err != nil {
logger.Error("Failed to display select storage prompt\nDetails: %v", err)
return
}
cfg.Storage.Type = storageType

// Save config //
cfg := config.GetConfig()
cfg.DB.Type = dbType
cfg.DB.Host = dbHost
cfg.DB.Port = dbPort
cfg.DB.Name = dbName
cfg.DB.Username = dbUser
cfg.DB.Password = dbPass
if storageType == config.CLOUD {
cloudType, err := ui.DisplaySelectCloudTypePrompt()
if err != nil {
logger.Error("Failed to display select cloud prompt\nDetails: %v", err)
return
}
cfg.Storage.Cloud.Type = cloudType

cfg.Storage.Type = storageType
if cloudType == config.AWS {
awsRegion, err := ui.DisplaySelectAWSRegionPrompt()
if err != nil {
logger.Error("Failed to display input aws region prompt\nDetails: %v", err)
return
}
cfg.Storage.Cloud.AWS.Region = awsRegion

awsBucketName, err := ui.DisplayInputAWSBucketNamePrompt()
if err != nil {
logger.Error("Failed to display input aws bucket name prompt\nDetails: %v", err)
return
}
cfg.Storage.Cloud.AWS.BucketName = awsBucketName

awsAccessKeyId, err := ui.DisplayInputAWSAccessKeyIdPrompt()
if err != nil {
logger.Error("Failed to display input aws access key id prompt\nDetails: %v", err)
return
}
cfg.Storage.Cloud.AWS.AccessKeyId = awsAccessKeyId

awsAccessKeySecret, err := ui.DisplayInputAWSAccessKeySecretPrompt()
if err != nil {
logger.Error("Failed to display input aws access key secret prompt\nDetails: %v", err)
return
}
cfg.Storage.Cloud.AWS.AccessKeySecret = awsAccessKeySecret

awsEndpoint, err := ui.DisplayInputAWSEndpointPrompt()
if err != nil {
logger.Error("Failed to display input aws endpoint prompt\nDetails: %v", err)
return
}
cfg.Storage.Cloud.AWS.Endpoint = awsEndpoint
}
}

if err = config.Save(cfg); err != nil {
logger.Error("Failed to save config\nDetails: %v", err)
Expand Down
91 changes: 91 additions & 0 deletions internal/storage/aws_cloud_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package storage

import (
"bytes"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/sboy99/go-vault/config"
"github.com/sboy99/go-vault/pkg/utils"
)

type AWSCloudStorage struct {
Region string
BucketName string
AccessKeyId string
AccessKeySecret string
Endpoint string
Token string
}

func NewAWSCloudStorage() *AWSCloudStorage {
cfg := config.GetConfig()
return &AWSCloudStorage{
Region: cfg.Storage.Cloud.AWS.Region,
BucketName: cfg.Storage.Cloud.AWS.BucketName,
AccessKeyId: cfg.Storage.Cloud.AWS.AccessKeyId,
AccessKeySecret: cfg.Storage.Cloud.AWS.AccessKeySecret,
Endpoint: cfg.Storage.Cloud.AWS.Endpoint,
Token: "", // TODO: Add token but not required for now
}
}

func (a *AWSCloudStorage) Upload(filename string, data []byte) error {
s3, err := a.getS3()
if err != nil {
return err
}
key := a.buildKey(filename)
s3Object := a.createPutObjectInput(key, data)
if _, err = s3.PutObject(s3Object); err != nil {
return err
}
return nil
}

func (a *AWSCloudStorage) Download(filename string) ([]byte, error) {
return nil, fmt.Errorf("Not Impl")
}

func (a *AWSCloudStorage) Delete(filename string) error {
return fmt.Errorf("Not Impl")
}

func (a *AWSCloudStorage) getS3() (*s3.S3, error) {
sess, err := a.getSession()
if err != nil {
return nil, err
}
return s3.New(sess), nil
}

func (a *AWSCloudStorage) getSession() (*session.Session, error) {
creds := credentials.NewStaticCredentials(a.AccessKeyId, a.AccessKeySecret, a.Token)
if a.Endpoint == "default" {
return session.NewSession(&aws.Config{
Region: aws.String(a.Region),
Credentials: creds,
})
}
return session.NewSession(&aws.Config{
Region: aws.String(a.Region),
Endpoint: aws.String(a.Endpoint),
S3ForcePathStyle: aws.Bool(true),
Credentials: creds,
})
}

func (a *AWSCloudStorage) createPutObjectInput(key string, data []byte) *s3.PutObjectInput {
return &s3.PutObjectInput{
Key: aws.String(key),
Bucket: aws.String(a.BucketName),
Body: bytes.NewReader(data),
}
}

func (a *AWSCloudStorage) buildKey(filename string) string {
return utils.GetUnixTimeStamp() + "_" + filename
}
Loading

0 comments on commit cd6e4aa

Please sign in to comment.