diff --git a/cmd/cmd.go b/cmd/cmd.go index ec7f2d5..809073d 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -6,6 +6,7 @@ import ( "os" "time" + "github.com/Masterminds/semver" "github.com/go-flucky/flucky/cmd/compression" "github.com/go-flucky/flucky/cmd/convert" "github.com/go-flucky/flucky/cmd/daemon" @@ -62,8 +63,8 @@ var rootCmd = &cobra.Command{ } // Execute a -func Execute(version string) { - rootCmd.Version = version +func Execute(version *semver.Version) { + rootCmd.Version = version.String() rootCmd.PersistentFlags().StringVar(&configFile, "config", "/etc/flucky/config.json", "Config file") diff --git a/cmd/compression/compression.go b/cmd/compression/compression.go index 463bb24..f52174c 100644 --- a/cmd/compression/compression.go +++ b/cmd/compression/compression.go @@ -3,13 +3,18 @@ package compression import ( "log" + "github.com/Masterminds/semver" "github.com/go-flucky/flucky/pkg/logfile" "github.com/spf13/cobra" ) -var compression bool -var configFile *string +var ( + compression bool + configFile *string + + version *semver.Version +) var compressionCmd = &cobra.Command{ Use: "compression", @@ -33,8 +38,10 @@ var compressionCmd = &cobra.Command{ }, } -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sverion *semver.Version) { configFile = cnfFile + version = sverion + cmd.AddCommand(compressionCmd) } diff --git a/cmd/convert/convert.go b/cmd/convert/convert.go index 554c88f..49cbe6f 100644 --- a/cmd/convert/convert.go +++ b/cmd/convert/convert.go @@ -3,13 +3,18 @@ package convert import ( "log" + "github.com/Masterminds/semver" "github.com/go-flucky/flucky/pkg/logfile" "github.com/spf13/cobra" ) -var compression bool -var configFile *string +var ( + compression bool + configFile *string + + version *semver.Version +) var convertCmd = &cobra.Command{ Use: "convert", @@ -37,9 +42,10 @@ var convertCmd = &cobra.Command{ } // Execute a -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion + cmd.AddCommand(convertCmd) convertCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values") - } diff --git a/cmd/daemon/daemon.go b/cmd/daemon/daemon.go index bbd1bfb..3a8091f 100644 --- a/cmd/daemon/daemon.go +++ b/cmd/daemon/daemon.go @@ -4,17 +4,22 @@ import ( "log" "time" + "github.com/Masterminds/semver" "github.com/go-flucky/flucky/pkg/config" "github.com/go-flucky/flucky/pkg/daemon" "github.com/go-flucky/flucky/pkg/logger" "github.com/spf13/cobra" ) -var cleanCacheInterval string -var compression bool -var configFile *string -var round float64 -var temperatureUnit string +var ( + cleanCacheInterval string + compression bool + configFile *string + round float64 + temperatureUnit string + + version *semver.Version +) var daemonCmd = &cobra.Command{ Use: "daemon", @@ -38,8 +43,10 @@ var daemonCmd = &cobra.Command{ }, } -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sverion *semver.Version) { configFile = cnfFile + version = sversion + cmd.AddCommand(daemonCmd) daemonCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values") daemonCmd.Flags().StringVar(&cleanCacheInterval, "clean-cache-interval", "5m", "Minute intervall to clean cache and write measured values into logfile") diff --git a/cmd/db/db.go b/cmd/db/db.go index 6eb630e..9943f46 100644 --- a/cmd/db/db.go +++ b/cmd/db/db.go @@ -4,12 +4,17 @@ import ( "context" "log" + "github.com/Masterminds/semver" database "github.com/go-flucky/flucky/pkg/db" "github.com/go-flucky/flucky/pkg/types" "github.com/spf13/cobra" ) -var configFile *string +var ( + configFile *string + + version *semver.Version +) var dbCmd = &cobra.Command{ Use: "db", @@ -41,8 +46,9 @@ var dbCmd = &cobra.Command{ } // Execute a -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion cmd.AddCommand(dbCmd) diff --git a/cmd/humidity/humidity.go b/cmd/humidity/humidity.go index ac7ed9f..95c9ad5 100644 --- a/cmd/humidity/humidity.go +++ b/cmd/humidity/humidity.go @@ -1,12 +1,17 @@ package humidity import ( + "github.com/Masterminds/semver" "github.com/spf13/cobra" ) -var compression bool -var configFile *string -var round float64 +var ( + compression bool + configFile *string + round float64 + + version *semver.Version +) var humidityCmd = &cobra.Command{ Use: "humidity", @@ -14,8 +19,9 @@ var humidityCmd = &cobra.Command{ } // Execute a -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion cmd.AddCommand(humidityCmd) diff --git a/cmd/pressure/pressure.go b/cmd/pressure/pressure.go index 4f75206..07ac38f 100644 --- a/cmd/pressure/pressure.go +++ b/cmd/pressure/pressure.go @@ -1,12 +1,17 @@ package pressure import ( + "github.com/Masterminds/semver" "github.com/spf13/cobra" ) -var compression bool -var configFile *string -var round float64 +var ( + compression bool + configFile *string + round float64 + + version *semver.Version +) var pressureCmd = &cobra.Command{ Use: "pressure", @@ -14,8 +19,9 @@ var pressureCmd = &cobra.Command{ } // Execute a -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion cmd.AddCommand(pressureCmd) diff --git a/cmd/rgbled/rgbled.go b/cmd/rgbled/rgbled.go index 340797e..379f4d2 100644 --- a/cmd/rgbled/rgbled.go +++ b/cmd/rgbled/rgbled.go @@ -1,10 +1,15 @@ package rgbled import ( + "github.com/Masterminds/semver" "github.com/spf13/cobra" ) -var configFile *string +var ( + configFile *string + + version *semver.Version +) var rgbLedCmd = &cobra.Command{ Use: "rgb-led", @@ -12,8 +17,9 @@ var rgbLedCmd = &cobra.Command{ } // InitCmd da -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion cmd.AddCommand(rgbLedCmd) diff --git a/cmd/sensor/sensor.go b/cmd/sensor/sensor.go index cb2c87a..963809c 100644 --- a/cmd/sensor/sensor.go +++ b/cmd/sensor/sensor.go @@ -1,10 +1,15 @@ package sensor import ( + "github.com/Masterminds/semver" "github.com/spf13/cobra" ) -var configFile *string +var ( + configFile *string + + version *semver.Version +) var sensorCmd = &cobra.Command{ Use: "sensor", @@ -12,8 +17,9 @@ var sensorCmd = &cobra.Command{ } // InitCmd da -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion cmd.AddCommand(sensorCmd) diff --git a/cmd/temperature/temperature.go b/cmd/temperature/temperature.go index 9417ba0..47d0f0c 100644 --- a/cmd/temperature/temperature.go +++ b/cmd/temperature/temperature.go @@ -3,12 +3,17 @@ package temperature import ( "fmt" + "github.com/Masterminds/semver" "github.com/spf13/cobra" ) -var compression bool -var configFile *string -var round float64 +var ( + compression bool + configFile *string + round float64 + + version *semver.Version +) var temperatureCmd = &cobra.Command{ Use: "temperature", @@ -17,8 +22,10 @@ var temperatureCmd = &cobra.Command{ } // Execute a -func InitCmd(cmd *cobra.Command, cnfFile *string) { +func InitCmd(cmd *cobra.Command, cnfFile *string, sversion *semver.Version) { configFile = cnfFile + version = sversion + cmd.AddCommand(temperatureCmd) } diff --git a/go.mod b/go.mod index 9440c3f..b5e6b33 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/go-flucky/flucky go 1.12 require ( + github.com/Masterminds/semver v1.4.2 github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375 github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e @@ -15,6 +16,7 @@ require ( github.com/spf13/pflag v1.0.3 // indirect github.com/stianeikeland/go-rpio v4.2.0+incompatible github.com/stretchr/testify v1.3.0 + golang.org/x/tools v0.0.0-20190830223141-573d9926052a gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect periph.io/x/periph v3.4.0+incompatible ) diff --git a/go.sum b/go.sum index 330c190..0570bea 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375 h1:vdUOwcZdV+bBfGUUh5oPPWSzw9p+lBnNSuGgQwGpCH4= github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375/go.mod h1:3iz1WHlYJU9b4NJei+Q8G7DN3K05arcCMlOQ+qNCDjo= github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce h1:Dog7PLNz1fPaXqHPOHonpERqsF57Oh4X76pM80T1GDY= @@ -30,6 +32,14 @@ github.com/stianeikeland/go-rpio v4.2.0+incompatible/go.mod h1:Sh81rdJwD96E2wja2 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190830223141-573d9926052a h1:XAHT1kdPpnU8Hk+FPi42KZFhtNFEk4vBg1U4OmIeHTU= +golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= periph.io/x/periph v3.4.0+incompatible h1:5gzxE4ryPq52cdqSw0mErR6pyJK8cBF2qdUAcOWh0bo= diff --git a/main.go b/main.go index 70d36f1..c3efa12 100644 --- a/main.go +++ b/main.go @@ -5,10 +5,14 @@ import ( "os" "os/user" + "github.com/Masterminds/semver" + "github.com/go-flucky/flucky/cmd" ) -var version string +var ( + version string +) func main() { @@ -23,5 +27,11 @@ func main() { os.Exit(1) } + sversion, err := semver.NewVersion(version) + if err != nil { + log.Println("The sematic versioning is invalid: %v", version) + os.Exit(1) + } + cmd.Execute(version) } diff --git a/pkg/db/db.go b/pkg/db/db.go index 949c0a6..fd422e6 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -7,17 +7,6 @@ import ( _ "github.com/lib/pq" ) -type DBOType string - -func (dboType DBOType) String() string { - return string(dboType) -} - -const ( - DBOTypePostgres DBOType = "postgres" - DBOTypeOracle = "oracle" -) - func New(dboType DBOType, host string, port string, database string, user string, password string) (Database, error) { connStr := fmt.Sprintf("%v://%v:%v@%v:%v/%v?sslmode=disable", dboType.String(), user, password, host, port, database) newDBO, err := sql.Open(dboType.String(), connStr) diff --git a/pkg/db/dbotype.go b/pkg/db/dbotype.go new file mode 100644 index 0000000..bccc59c --- /dev/null +++ b/pkg/db/dbotype.go @@ -0,0 +1,12 @@ +package db + +type DBOType string + +func (dboType DBOType) String() string { + return string(dboType) +} + +const ( + DBOTypePostgres DBOType = "postgres" + DBOTypeOracle = "oracle" +) diff --git a/pkg/db/interfaces.go b/pkg/db/interfaces.go index e68f9da..3f3c23b 100644 --- a/pkg/db/interfaces.go +++ b/pkg/db/interfaces.go @@ -3,6 +3,7 @@ package db import ( "context" + "github.com/Masterminds/semver" "github.com/go-flucky/flucky/pkg/types" ) @@ -11,6 +12,9 @@ type Database interface { // Close DB Connction Close() error + // Schema + Schema(ctx context.Context, version *semver.Version) error + // Delete DeleteDevices(ctx context.Context, devices []*types.Device) error DeleteMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error diff --git a/pkg/db/postgres.go b/pkg/db/postgres.go index 37cecdc..26d9e3c 100644 --- a/pkg/db/postgres.go +++ b/pkg/db/postgres.go @@ -4,6 +4,10 @@ import ( "context" "database/sql" "fmt" + "path/filepath" + "strings" + + "github.com/Masterminds/semver" "github.com/go-flucky/flucky/pkg/types" _ "github.com/lib/pq" @@ -17,6 +21,61 @@ func (p *Postgres) Close() error { return p.Close() } +// Schema create or upgrade database schema to the version of the flucky binary +func (p *Postgres) Schema(ctx context.Context, version *semver.Version) error { + + query := "SELECT value FROM info WHERE key='version';" + + stmt, err := p.dbo.PrepareContext(ctx, query) + if err != nil { + + asset := "pkg/db/sql/psql/schema.sql" + queryBytes, err := Asset(asset) + if err != nil { + return fmt.Errorf("%v: %v", errorGetAsset, err) + } + query = string(queryBytes) + + _, err = p.dbo.ExecContext(ctx, query) + if err != nil { + return fmt.Errorf("%v: %v", errorStatementExecute, err) + } + + return nil + } + + schemaVersion := "" + row := stmt.QueryRowContext(ctx) + err = row.Scan(&schemaVersion) + if err != nil { + return fmt.Errorf("%v: %v", errorScanRow, err) + } + + fromVersion, err := semver.NewVersion(schemaVersion) + if err != nil { + return fmt.Errorf("Can not create new sematic version from database: %v", err) + } + + if err := schema(fromVersion, version); err != nil { + return err + } + + // The result will be 0 if from == to, -1 if from < to, or +1 if from > to. + switch fromVersion.Compare(version) { + case -1: + return schema(fromVersion, version) + case 0: + // fromVersion and toVersion are equal + return nil + case 1: + // fromVersion < toVersion + return fmt.Errorf("Can not downgrade the database schema. Update the flucky binary") + } + + return nil + +} + func (p *Postgres) DeleteDevices(ctx context.Context, devices []*types.Device) error { asset := "pkg/db/sql/psql/deleteDevice.sql" queryBytes, err := Asset(asset) @@ -479,3 +538,42 @@ func (p *Postgres) UpdateMeasuredValues(ctx context.Context, measuredValues []*t func (p *Postgres) UpdateSensors(ctx context.Context, sensots []*types.Sensor) error { return nil } + +func schema(fromVersion *semver.Version, toVersion *semver.Version) error { + + sqlAssetFiles, err := parseAssetDir("pkg/db/sql/psql/schema") + if err != nil { + return fmt.Errorf("Can not restore sql files: %v", err) + } + + for _, sqlAssetFile := range sqlAssetFiles { + + version := strings.Replace() + + sqlSchemaInformations := &schemaInformation{ + Version: filepath.Split() + Asset: sqlAssetFile, + } + + } + + return nil +} + +func parseAssetDir(dir string) ([]string, error) { + assetFiles, err := AssetDir(dir) + if err != nil { + return nil, fmt.Errorf("Can not load asset directory %v: %v", dir, err) + } + + goldenAssetFiles := assetFiles + + for i, assetFile := range assetFiles { + if strings.HasPrefix(assetFile, "/") { + // is directory- SKIP + goldenAssetFiles = append(goldenAssetFiles[:i], goldenAssetFiles[i+1:]...) + } + } + + return goldenAssetFiles, nil +} diff --git a/pkg/db/postgres_test.go b/pkg/db/postgres_test.go index cfb3a74..893e7c5 100644 --- a/pkg/db/postgres_test.go +++ b/pkg/db/postgres_test.go @@ -80,53 +80,57 @@ func TestPostgres(t *testing.T) { tests := []*test{ &test{ - Name: "insertDevices", - Test: testInsertDevices, - }, - &test{ - Name: "insertSensors", - Test: testInsertSensors, - }, - &test{ - Name: "insertHumidity", - Test: testInsertHumidity, - }, - &test{ - Name: "insertPressure", - Test: testInsertPressure, - }, - &test{ - Name: "insertTemperatures", - Test: testInsertTemperatures, - }, - &test{ - Name: "deleteHumidities", - Test: testDeleteHumidity, - }, - &test{ - Name: "deletePressures", - Test: testDeletePressures, - }, - &test{ - Name: "deleteTemperatures", - Test: testDeleteTemperatures, - }, - &test{ - Name: "insertMeasuredValues", - Test: testInsertMeasuredValues, - }, - &test{ - Name: "deleteMeasuredValues", - Test: testDeleteMeasuredValues, - }, - &test{ - Name: "deleteSensors", - Test: testDeleteSensors, - }, - &test{ - Name: "deleteDevices", - Test: testDeleteDevices, + Name: "schema", + Test: testSchemaCreate, }, + // &test{ + // Name: "insertDevices", + // Test: testInsertDevices, + // }, + // &test{ + // Name: "insertSensors", + // Test: testInsertSensors, + // }, + // &test{ + // Name: "insertHumidity", + // Test: testInsertHumidity, + // }, + // &test{ + // Name: "insertPressure", + // Test: testInsertPressure, + // }, + // &test{ + // Name: "insertTemperatures", + // Test: testInsertTemperatures, + // }, + // &test{ + // Name: "deleteHumidities", + // Test: testDeleteHumidity, + // }, + // &test{ + // Name: "deletePressures", + // Test: testDeletePressures, + // }, + // &test{ + // Name: "deleteTemperatures", + // Test: testDeleteTemperatures, + // }, + // &test{ + // Name: "insertMeasuredValues", + // Test: testInsertMeasuredValues, + // }, + // &test{ + // Name: "deleteMeasuredValues", + // Test: testDeleteMeasuredValues, + // }, + // &test{ + // Name: "deleteSensors", + // Test: testDeleteSensors, + // }, + // &test{ + // Name: "deleteDevices", + // Test: testDeleteDevices, + // }, } for _, test := range tests { @@ -134,6 +138,13 @@ func TestPostgres(t *testing.T) { } } +func testSchemaCreate(t *testing.T) { + require := require.New(t) + ctx := context.Background() + err := database.Schema(ctx, "v0.1.0") + require.NoError(err) +} + func testInsertDevices(t *testing.T) { require := require.New(t) ctx := context.Background() diff --git a/pkg/db/schema.go b/pkg/db/schema.go new file mode 100644 index 0000000..1faf805 --- /dev/null +++ b/pkg/db/schema.go @@ -0,0 +1,10 @@ +package db + +var ( + postgreSQLSchemata []*schemaInformation +) + +type schemaInformation struct { + Version string + Asset string +} diff --git a/pkg/db/sql/psql/schema/latest.sql b/pkg/db/sql/psql/schema/latest.sql new file mode 100644 index 0000000..e69de29 diff --git a/pkg/db/sql/psql/schema.sql b/pkg/db/sql/psql/schema/v0.1.0.sql similarity index 95% rename from pkg/db/sql/psql/schema.sql rename to pkg/db/sql/psql/schema/v0.1.0.sql index 3f13721..4259165 100644 --- a/pkg/db/sql/psql/schema.sql +++ b/pkg/db/sql/psql/schema/v0.1.0.sql @@ -3,6 +3,7 @@ DROP TABLE IF EXISTS sensors CASCADE; DROP TABLE IF EXISTS humidities CASCADE; DROP TABLE IF EXISTS pressures CASCADE; DROP TABLE IF EXISTS temperatures CASCADE; +DROP TABLE IF EXISTS info CASCADE; -- +----------------------------------------+ @@ -61,6 +62,11 @@ CREATE TABLE IF NOT EXISTS temperatures ( update_date TIMESTAMP WITH TIME ZONE ); +CREATE TABLE IF NOT EXISTS info ( + key VARCHAR(32) CONSTRAINT pk_info PRIMARY KEY, + value VARCHAR(32) NOT NULL +); + -- +----------------------------------------+ -- | FOREIGN-KEYS | -- +----------------------------------------+