From dd7ea3156efe847ef5d83f3e918cfd8c293aa78f Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Mon, 19 Nov 2018 22:36:21 +0100 Subject: [PATCH] fix: set config, set remote --- Gopkg.lock | 9 + Gopkg.toml | 8 + Makefile | 6 +- cmd/cmd.go | 11 +- cmd/config/config.go | 19 ++ cmd/config/create.go | 26 ++ cmd/remote/add.go | 30 +++ cmd/remote/list.go | 24 ++ cmd/remote/register.go | 23 ++ cmd/remote/remote.go | 18 ++ cmd/remote/remove.go | 33 +++ cmd/temperature/get.go | 25 ++ cmd/temperature/temperature.go | 16 +- pkg/config/config.go | 99 ++++++++ pkg/remote/remote.go | 167 ++++++++++++ pkg/temperature/temperature.go | 42 +-- pkg/types/types.go | 48 ++++ vendor/github.com/satori/go.uuid/.travis.yml | 23 ++ vendor/github.com/satori/go.uuid/LICENSE | 20 ++ vendor/github.com/satori/go.uuid/README.md | 65 +++++ vendor/github.com/satori/go.uuid/codec.go | 206 +++++++++++++++ vendor/github.com/satori/go.uuid/generator.go | 239 ++++++++++++++++++ vendor/github.com/satori/go.uuid/sql.go | 78 ++++++ vendor/github.com/satori/go.uuid/uuid.go | 161 ++++++++++++ 24 files changed, 1338 insertions(+), 58 deletions(-) create mode 100644 cmd/config/config.go create mode 100644 cmd/config/create.go create mode 100644 cmd/remote/add.go create mode 100644 cmd/remote/list.go create mode 100644 cmd/remote/register.go create mode 100644 cmd/remote/remote.go create mode 100644 cmd/remote/remove.go create mode 100644 cmd/temperature/get.go create mode 100644 pkg/config/config.go create mode 100644 pkg/remote/remote.go create mode 100644 pkg/types/types.go create mode 100644 vendor/github.com/satori/go.uuid/.travis.yml create mode 100644 vendor/github.com/satori/go.uuid/LICENSE create mode 100644 vendor/github.com/satori/go.uuid/README.md create mode 100644 vendor/github.com/satori/go.uuid/codec.go create mode 100644 vendor/github.com/satori/go.uuid/generator.go create mode 100644 vendor/github.com/satori/go.uuid/sql.go create mode 100644 vendor/github.com/satori/go.uuid/uuid.go diff --git a/Gopkg.lock b/Gopkg.lock index 4dc1db1..60d4f0b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -17,6 +17,14 @@ revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" +[[projects]] + digest = "1:274f67cb6fed9588ea2521ecdac05a6d62a8c51c074c1fccc6a49a40ba80e925" + name = "github.com/satori/go.uuid" + packages = ["."] + pruneopts = "UT" + revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" + version = "v1.2.0" + [[projects]] digest = "1:645cabccbb4fa8aab25a956cbcbdf6a6845ca736b2c64e197ca7cbb9d210b939" name = "github.com/spf13/cobra" @@ -38,6 +46,7 @@ analyzer-version = 1 input-imports = [ "git.cryptic.systems/fh-trier/go-flucky-server/pkg/types", + "github.com/satori/go.uuid", "github.com/spf13/cobra", ] solver-name = "gps-cdcl" diff --git a/Gopkg.toml b/Gopkg.toml index f2347c2..0c75165 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -25,6 +25,14 @@ # unused-packages = true +[[constraint]] + branch = "master" + name = "git.cryptic.systems/fh-trier/go-flucky-server" + +[[constraint]] + name = "github.com/satori/go.uuid" + version = "1.2.0" + [[constraint]] name = "github.com/spf13/cobra" version = "0.0.3" diff --git a/Makefile b/Makefile index 7b6df7d..ee6508c 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ VERSION ?= "$(shell git rev-parse --short HEAD)-git" -go-build-arm7: - GOOS=linux GOARCH=arm GOARM=7 go build -o flucky -ldflags "-X main.version=${VERSION}" - go-build-amd: go build -o flucky -ldflags "-X main.version=${VERSION}" +go-build-arm7: + GOOS=linux GOARCH=arm GOARM=7 go build -o flucky -ldflags "-X main.version=${VERSION}" + pi-copy: go-build-arm7 scp flucky hades:/usr/local/bin ssh hades 'chmod +x /usr/local/bin/flucky' diff --git a/cmd/cmd.go b/cmd/cmd.go index 7ad4857..2636f26 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,10 +1,13 @@ package cmd import ( - "git.cryptic.systems/fh-trier/go-flucky/cmd/temperature" + "git.cryptic.systems/fh-trier/go-flucky/cmd/config" + "git.cryptic.systems/fh-trier/go-flucky/cmd/remote" "github.com/spf13/cobra" ) +var configDir string + var rootCmd = &cobra.Command{ Use: "flucky", Short: "Read from sensors", @@ -14,7 +17,11 @@ var rootCmd = &cobra.Command{ func Execute(version string) { rootCmd.Version = version - temperature.InitCmd(rootCmd) + rootCmd.PersistentFlags().StringVarP(&configDir, "config", "c", "/etc/flucky", "The base directory for all configuration files.") + + config.InitCmd(rootCmd, configDir) + remote.InitCmd(rootCmd, configDir) + //temperature.InitCmd(rootCmd) rootCmd.Execute() } diff --git a/cmd/config/config.go b/cmd/config/config.go new file mode 100644 index 0000000..d85156e --- /dev/null +++ b/cmd/config/config.go @@ -0,0 +1,19 @@ +package config + +import ( + "github.com/spf13/cobra" +) + +var configDir string + +var configCmd = &cobra.Command{ + Use: "config", + Short: "config", +} + +func InitCmd(cmd *cobra.Command, c string) { + configDir = c + + cmd.AddCommand(configCmd) + +} diff --git a/cmd/config/create.go b/cmd/config/create.go new file mode 100644 index 0000000..9227288 --- /dev/null +++ b/cmd/config/create.go @@ -0,0 +1,26 @@ +package config + +import ( + "log" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/config" + "github.com/spf13/cobra" +) + +var force bool + +var createConfigCmd = &cobra.Command{ + Use: "create", + Short: "create", + Run: func(cmd *cobra.Command, args []string) { + + if err := config.Create(configDir, force); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + configCmd.AddCommand(createConfigCmd) + createConfigCmd.Flags().BoolVarP(&force, "force", "f", false, "Force the creation of a new configuration") +} diff --git a/cmd/remote/add.go b/cmd/remote/add.go new file mode 100644 index 0000000..ae2bd6f --- /dev/null +++ b/cmd/remote/add.go @@ -0,0 +1,30 @@ +package remote + +import ( + "log" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/remote" + "git.cryptic.systems/fh-trier/go-flucky/pkg/types" + "github.com/spf13/cobra" +) + +var addRemoteCmd = &cobra.Command{ + Use: "add", + Short: "add", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + + remoteObject := types.Remote{ + Name: args[0], + Address: args[1], + } + + if err := remote.Add(&remoteObject, configDir); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + remoteCmd.AddCommand(addRemoteCmd) +} diff --git a/cmd/remote/list.go b/cmd/remote/list.go new file mode 100644 index 0000000..f7ee3f9 --- /dev/null +++ b/cmd/remote/list.go @@ -0,0 +1,24 @@ +package remote + +import ( + "log" + "os" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/remote" + "github.com/spf13/cobra" +) + +var listRemoteCmd = &cobra.Command{ + Use: "list", + Short: "list", + Run: func(cmd *cobra.Command, args []string) { + + if err := remote.List(os.Stdout, configDir); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + remoteCmd.AddCommand(listRemoteCmd) +} diff --git a/cmd/remote/register.go b/cmd/remote/register.go new file mode 100644 index 0000000..78680cc --- /dev/null +++ b/cmd/remote/register.go @@ -0,0 +1,23 @@ +package remote + +import ( + "log" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/remote" + "github.com/spf13/cobra" +) + +var registerRemoteCmd = &cobra.Command{ + Use: "register", + Short: "register on remote servers", + Run: func(cmd *cobra.Command, args []string) { + + if err := remote.SendDevice(configDir); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + remoteCmd.AddCommand(registerRemoteCmd) +} diff --git a/cmd/remote/remote.go b/cmd/remote/remote.go new file mode 100644 index 0000000..a89aefc --- /dev/null +++ b/cmd/remote/remote.go @@ -0,0 +1,18 @@ +package remote + +import ( + "github.com/spf13/cobra" +) + +var configDir string + +var remoteCmd = &cobra.Command{ + Use: "remote", + Short: "remote", +} + +func InitCmd(cmd *cobra.Command, c string) { + configDir = c + + cmd.AddCommand(remoteCmd) +} diff --git a/cmd/remote/remove.go b/cmd/remote/remove.go new file mode 100644 index 0000000..25b013b --- /dev/null +++ b/cmd/remote/remove.go @@ -0,0 +1,33 @@ +package remote + +import ( + "log" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/remote" + "github.com/spf13/cobra" +) + +var all bool + +var removeRemoteCmd = &cobra.Command{ + Use: "remove", + Short: "remove", + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + + if all { + if err := remote.RemoveAll(configDir); err != nil { + log.Fatal(err) + } + } else { + if err := remote.Remove(args[0], configDir); err != nil { + log.Fatal(err) + } + } + }, +} + +func init() { + remoteCmd.AddCommand(removeRemoteCmd) + removeRemoteCmd.Flags().BoolVarP(&all, "all", "a", false, "Select all remote connections") +} diff --git a/cmd/temperature/get.go b/cmd/temperature/get.go new file mode 100644 index 0000000..bbe7e1d --- /dev/null +++ b/cmd/temperature/get.go @@ -0,0 +1,25 @@ +package remote + +import ( + "log" + "os" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/temperature" + "github.com/spf13/cobra" +) + +var getTemperatureCmd = &cobra.Command{ + Use: "get", + Short: "get temperature from sensor", + // Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + + if err := temperature.Get(os.Stdout); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + temperatureCmd.AddCommand(getTemperatureCmd) +} diff --git a/cmd/temperature/temperature.go b/cmd/temperature/temperature.go index aa1e689..4ddbb0d 100644 --- a/cmd/temperature/temperature.go +++ b/cmd/temperature/temperature.go @@ -1,24 +1,16 @@ -package temperature +package remote import ( - "log" - "os" - - "git.cryptic.systems/fh-trier/go-flucky/pkg/temperature" "github.com/spf13/cobra" ) var temperatureCmd = &cobra.Command{ Use: "temperature", - Short: "temperature", - Run: func(cmd *cobra.Command, args []string) { - if err := temperature.Print(os.Stdout); err != nil { - log.Fatal(err) - } - }, + Short: "Read temperature from sensor", } -func InitCmd(cmd *cobra.Command) { +// Execute a +func Init(cmd *cobra.Command) { cmd.AddCommand(temperatureCmd) diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..8ac2755 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,99 @@ +package config + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/satori/go.uuid" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/types" +) + +var configFilename = "config.json" + +func Create(configDir string, force bool) error { + + configPath := filepath.Join(configDir, configFilename) + + config := &types.Config{ + DeviceID: uuid.NewV4().String(), + } + + // If no config file exists, create a new one on the location + + if !force { + if _, err := os.Stat(configPath); !os.IsNotExist(err) { + return fmt.Errorf("%v already exists. Use -f to overwrite", configPath) + } + } + + if _, err := os.Stat(configPath); os.IsNotExist(err) { + err := os.MkdirAll(configDir, os.ModePerm) + if err != nil { + return fmt.Errorf("Can not create directory: %v", err) + } + } + + f, err := os.Create(configPath) + if err != nil { + return fmt.Errorf("Can not create config: %v", err) + } + defer f.Close() + + config.JSONWriter(f) + + return nil + +} + +func Read(configDir string) (*types.Config, error) { + + configPath := filepath.Join(configDir, configFilename) + + var config types.Config + + // If no config file exists, create a new one on the location + if _, err := os.Stat(configPath); os.IsNotExist(err) { + return nil, fmt.Errorf("Can not find config %v: %v", configPath, err) + } + + // open config file + jsonFile, err := os.Open(configPath) + if err != nil { + return nil, fmt.Errorf("Can not open file %v: %v", configPath, err) + } + defer jsonFile.Close() + + jsonParser := json.NewDecoder(jsonFile) + if err := jsonParser.Decode(&config); err != nil { + return nil, err + } + + return &config, nil +} + +func Write(config *types.Config, configDir string) error { + + configPath := filepath.Join(configDir, configFilename) + + // If no config file exists, create a new one on the location + if _, err := os.Stat(configPath); os.IsNotExist(err) { + return fmt.Errorf("Can not find config %v: %v", configPath, err) + } + + // open config file + jsonFile, err := os.Create(configPath) + if err != nil { + return fmt.Errorf("Can not open file %v: %v", configPath, err) + } + defer jsonFile.Close() + + err = config.JSONWriter(jsonFile) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go new file mode 100644 index 0000000..5470d9b --- /dev/null +++ b/pkg/remote/remote.go @@ -0,0 +1,167 @@ +package remote + +import ( + "bytes" + "fmt" + "io" + "net/http" + "text/tabwriter" + + stypes "git.cryptic.systems/fh-trier/go-flucky-server/pkg/types" + "git.cryptic.systems/fh-trier/go-flucky/pkg/config" + "git.cryptic.systems/fh-trier/go-flucky/pkg/types" +) + +func Add(remote *types.Remote, configDir string) error { + + configuration, err := config.Read(configDir) + if err != nil { + return err + } + + // search after duplicate remote_names + for _, r := range configuration.Remotes { + if r.Name == remote.Name { + return fmt.Errorf("Remote-Name %v already exists", remote.Name) + } + } + + configuration.Remotes = append(configuration.Remotes, remote) + + if err := config.Write(configuration, configDir); err != nil { + return err + } + + return nil +} + +func List(w io.Writer, configDir string) error { + + configuration, err := config.Read(configDir) + if err != nil { + return err + } + + tw := tabwriter.NewWriter(w, 0, 0, 5, ' ', 0) + + fmt.Fprint(tw, "name\taddress\n") + + for _, remote := range configuration.Remotes { + fmt.Fprintf(tw, "%v\t%v\n", remote.Name, remote.Address) + } + + tw.Flush() + + return nil +} + +func Remove(name string, configDir string) error { + + con, err := config.Read(configDir) + if err != nil { + return err + } + + var j int + for _, remote := range con.Remotes { + if remote.Name == name { + con.Remotes = append(con.Remotes[:j], con.Remotes[j+1:]...) + + if j > 0 { + j = j - 1 + } + continue + } + j++ + } + + if err := config.Write(con, configDir); err != nil { + return err + } + + return nil +} + +func RemoveAll(configDir string) error { + + con, err := config.Read(configDir) + if err != nil { + return err + } + + con.Remotes = nil + + if err := config.Write(con, configDir); err != nil { + return err + } + + return nil +} + +func SendTemperature(temperature *stypes.Temperature, configDir string) error { + + con, err := config.Read(configDir) + if err != nil { + return err + } + + var buffer bytes.Buffer + temperature.EncodeToJSON(&buffer) + + for _, remote := range con.Remotes { + requestURL := fmt.Sprintf("%s%s", remote.Address, "/temperatures") + req, err := http.NewRequest("POST", requestURL, &buffer) + if err != nil { + return err + } + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return fmt.Errorf("Invalid HTTP-Statuscode - expected 200, got %d", resp.StatusCode) + } + } + + return nil + +} + +// SendDevice .. +func SendDevice(configDir string) error { + con, err := config.Read(configDir) + if err != nil { + return err + } + + device := new(stypes.Device) + device.DeviceID = con.DeviceID + + var buffer bytes.Buffer + device.EncodeToJSON(&buffer) + + for _, remote := range con.Remotes { + requestURL := fmt.Sprintf("%s%s", remote.Address, "/devices") + req, err := http.NewRequest("POST", requestURL, &buffer) + if err != nil { + return err + } + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return fmt.Errorf("Invalid HTTP-Statuscode - expected 200, got %d", resp.StatusCode) + } + } + + return nil +} diff --git a/pkg/temperature/temperature.go b/pkg/temperature/temperature.go index 9ea560c..2b20147 100644 --- a/pkg/temperature/temperature.go +++ b/pkg/temperature/temperature.go @@ -1,50 +1,10 @@ package temperature import ( - "bytes" - "fmt" "io" - "net/http" - - "git.cryptic.systems/fh-trier/go-flucky-server/pkg/types" ) -func Print(w io.Writer) error { - - t := types.Temperature{ - TemperatureID: "1", - TemperatureValue: "22.5", - TemperatureDate: "2018-10-15T23:31:28.132417Z", - DeviceID: "510abdfb-3d0d-4248-b0e5-94ee962c2532", - } - - err := postTemperature(&t) - if err != nil { - return err - } - return nil -} - -func postTemperature(temperature *types.Temperature) error { - var buffer bytes.Buffer - temperature.EncodeToJSON(&buffer) - - requestURL := fmt.Sprintf("%s://%s%s", "https", "flucky.cryptic.systems", "/temperatures") - req, err := http.NewRequest("POST", requestURL, &buffer) - if err != nil { - return err - } - - client := http.Client{} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return fmt.Errorf("Invalid HTTP-Statuscode - expected 200, got %d", resp.StatusCode) - } +func Get(w io.Writer) error { return nil } diff --git a/pkg/types/types.go b/pkg/types/types.go new file mode 100644 index 0000000..c9c6fc7 --- /dev/null +++ b/pkg/types/types.go @@ -0,0 +1,48 @@ +package types + +import ( + "bytes" + "encoding/json" + "fmt" + "io" +) + +type Config struct { + DeviceID string `json:"device_id"` + Remotes []*Remote `json:"remotes"` +} + +func (c *Config) ToJSON() (string, error) { + var b bytes.Buffer + err := c.JSONWriter(&b) + if err != nil { + return "", err + } + + return b.String(), nil +} + +// JSONWriter needs a writer to write the struct into json string +func (c *Config) JSONWriter(w io.Writer) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + err := encoder.Encode(&c) + if err != nil { + return fmt.Errorf("Error in encoding struct to json: %v", err) + } + return nil +} + +// JSONDecoder decode a json string from a reader into a struct +func (c *Config) JSONDecoder(r io.Reader) error { + jsonDecoder := json.NewDecoder(r) + if err := jsonDecoder.Decode(&c); err != nil { + return fmt.Errorf("Can not unmarshal JSON: %v", err) + } + return nil +} + +type Remote struct { + Name string `json:"remote_name"` + Address string `json:"remote_address"` +} diff --git a/vendor/github.com/satori/go.uuid/.travis.yml b/vendor/github.com/satori/go.uuid/.travis.yml new file mode 100644 index 0000000..20dd53b --- /dev/null +++ b/vendor/github.com/satori/go.uuid/.travis.yml @@ -0,0 +1,23 @@ +language: go +sudo: false +go: + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - tip +matrix: + allow_failures: + - go: tip + fast_finish: true +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -service=travis-ci +notifications: + email: false diff --git a/vendor/github.com/satori/go.uuid/LICENSE b/vendor/github.com/satori/go.uuid/LICENSE new file mode 100644 index 0000000..926d549 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2013-2018 by Maxim Bublis + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/satori/go.uuid/README.md b/vendor/github.com/satori/go.uuid/README.md new file mode 100644 index 0000000..7b1a722 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/README.md @@ -0,0 +1,65 @@ +# UUID package for Go language + +[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) +[![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid) +[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid) + +This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. + +With 100% test coverage and benchmarks out of box. + +Supported versions: +* Version 1, based on timestamp and MAC address (RFC 4122) +* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) +* Version 3, based on MD5 hashing (RFC 4122) +* Version 4, based on random numbers (RFC 4122) +* Version 5, based on SHA-1 hashing (RFC 4122) + +## Installation + +Use the `go` command: + + $ go get github.com/satori/go.uuid + +## Requirements + +UUID package requires Go >= 1.2. + +## Example + +```go +package main + +import ( + "fmt" + "github.com/satori/go.uuid" +) + +func main() { + // Creating UUID Version 4 + u1 := uuid.NewV4() + fmt.Printf("UUIDv4: %s\n", u1) + + // Parsing UUID from string input + u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + if err != nil { + fmt.Printf("Something gone wrong: %s", err) + } + fmt.Printf("Successfully parsed: %s", u2) +} +``` + +## Documentation + +[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. + +## Links +* [RFC 4122](http://tools.ietf.org/html/rfc4122) +* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) + +## Copyright + +Copyright (C) 2013-2018 by Maxim Bublis . + +UUID package released under MIT License. +See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/vendor/github.com/satori/go.uuid/codec.go b/vendor/github.com/satori/go.uuid/codec.go new file mode 100644 index 0000000..656892c --- /dev/null +++ b/vendor/github.com/satori/go.uuid/codec.go @@ -0,0 +1,206 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "bytes" + "encoding/hex" + "fmt" +) + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromBytesOrNil returns UUID converted from raw byte slice input. +// Same behavior as FromBytes, but returns a Nil UUID on error. +func FromBytesOrNil(input []byte) UUID { + uuid, err := FromBytes(input) + if err != nil { + return Nil + } + return uuid +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// FromStringOrNil returns UUID parsed from string input. +// Same behavior as FromString, but returns a Nil UUID on error. +func FromStringOrNil(input string) UUID { + uuid, err := FromString(input) + if err != nil { + return Nil + } + return uuid +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +// "6ba7b8109dad11d180b400c04fd430c8" +// ABNF for supported UUID text representation follows: +// uuid := canonical | hashlike | braced | urn +// plain := canonical | hashlike +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct +// hashlike := 12hexoct +// braced := '{' plain '}' +// urn := URN ':' UUID-NID ':' plain +// URN := 'urn' +// UUID-NID := 'uuid' +// 12hexoct := 6hexoct 6hexoct +// 6hexoct := 4hexoct 2hexoct +// 4hexoct := 2hexoct 2hexoct +// 2hexoct := hexoct hexoct +// hexoct := hexdig hexdig +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' +func (u *UUID) UnmarshalText(text []byte) (err error) { + switch len(text) { + case 32: + return u.decodeHashLike(text) + case 36: + return u.decodeCanonical(text) + case 38: + return u.decodeBraced(text) + case 41: + fallthrough + case 45: + return u.decodeURN(text) + default: + return fmt.Errorf("uuid: incorrect UUID length: %s", text) + } +} + +// decodeCanonical decodes UUID string in format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". +func (u *UUID) decodeCanonical(t []byte) (err error) { + if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + src := t[:] + dst := u[:] + + for i, byteGroup := range byteGroups { + if i > 0 { + src = src[1:] // skip dash + } + _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup]) + if err != nil { + return + } + src = src[byteGroup:] + dst = dst[byteGroup/2:] + } + + return +} + +// decodeHashLike decodes UUID string in format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeHashLike(t []byte) (err error) { + src := t[:] + dst := u[:] + + if _, err = hex.Decode(dst, src); err != nil { + return err + } + return +} + +// decodeBraced decodes UUID string in format +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format +// "{6ba7b8109dad11d180b400c04fd430c8}". +func (u *UUID) decodeBraced(t []byte) (err error) { + l := len(t) + + if t[0] != '{' || t[l-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + return u.decodePlain(t[1 : l-1]) +} + +// decodeURN decodes UUID string in format +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeURN(t []byte) (err error) { + total := len(t) + + urn_uuid_prefix := t[:9] + + if !bytes.Equal(urn_uuid_prefix, urnPrefix) { + return fmt.Errorf("uuid: incorrect UUID format: %s", t) + } + + return u.decodePlain(t[9:total]) +} + +// decodePlain decodes UUID string in canonical format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodePlain(t []byte) (err error) { + switch len(t) { + case 32: + return u.decodeHashLike(t) + case 36: + return u.decodeCanonical(t) + default: + return fmt.Errorf("uuid: incorrrect UUID length: %s", t) + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != Size { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} diff --git a/vendor/github.com/satori/go.uuid/generator.go b/vendor/github.com/satori/go.uuid/generator.go new file mode 100644 index 0000000..3f2f1da --- /dev/null +++ b/vendor/github.com/satori/go.uuid/generator.go @@ -0,0 +1,239 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "hash" + "net" + "os" + "sync" + "time" +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +var ( + global = newDefaultGenerator() + + epochFunc = unixTimeFunc + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() UUID { + return global.NewV1() +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) UUID { + return global.NewV2(domain) +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + return global.NewV3(ns, name) +} + +// NewV4 returns random generated UUID. +func NewV4() UUID { + return global.NewV4() +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + return global.NewV5(ns, name) +} + +// Generator provides interface for generating UUIDs. +type Generator interface { + NewV1() UUID + NewV2(domain byte) UUID + NewV3(ns UUID, name string) UUID + NewV4() UUID + NewV5(ns UUID, name string) UUID +} + +// Default generator implementation. +type generator struct { + storageOnce sync.Once + storageMutex sync.Mutex + + lastTime uint64 + clockSequence uint16 + hardwareAddr [6]byte +} + +func newDefaultGenerator() Generator { + return &generator{} +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func (g *generator) NewV1() UUID { + u := UUID{} + + timeNow, clockSeq, hardwareAddr := g.getStorage() + + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + copy(u[10:], hardwareAddr) + + u.SetVersion(V1) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func (g *generator) NewV2(domain byte) UUID { + u := UUID{} + + timeNow, clockSeq, hardwareAddr := g.getStorage() + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[0:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[0:], posixGID) + } + + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + u[9] = domain + + copy(u[10:], hardwareAddr) + + u.SetVersion(V2) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func (g *generator) NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(V3) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV4 returns random generated UUID. +func (g *generator) NewV4() UUID { + u := UUID{} + g.safeRandom(u[:]) + u.SetVersion(V4) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func (g *generator) NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(V5) + u.SetVariant(VariantRFC4122) + + return u +} + +func (g *generator) initStorage() { + g.initClockSequence() + g.initHardwareAddr() +} + +func (g *generator) initClockSequence() { + buf := make([]byte, 2) + g.safeRandom(buf) + g.clockSequence = binary.BigEndian.Uint16(buf) +} + +func (g *generator) initHardwareAddr() { + interfaces, err := net.Interfaces() + if err == nil { + for _, iface := range interfaces { + if len(iface.HardwareAddr) >= 6 { + copy(g.hardwareAddr[:], iface.HardwareAddr) + return + } + } + } + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence + g.safeRandom(g.hardwareAddr[:]) + + // Set multicast bit as recommended in RFC 4122 + g.hardwareAddr[0] |= 0x01 +} + +func (g *generator) safeRandom(dest []byte) { + if _, err := rand.Read(dest); err != nil { + panic(err) + } +} + +// Returns UUID v1/v2 storage state. +// Returns epoch timestamp, clock sequence, and hardware address. +func (g *generator) getStorage() (uint64, uint16, []byte) { + g.storageOnce.Do(g.initStorage) + + g.storageMutex.Lock() + defer g.storageMutex.Unlock() + + timeNow := epochFunc() + // Clock changed backwards since last UUID generation. + // Should increase clock sequence. + if timeNow <= g.lastTime { + g.clockSequence++ + } + g.lastTime = timeNow + + return timeNow, g.clockSequence, g.hardwareAddr[:] +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +// This is default epoch calculation function. +func unixTimeFunc() uint64 { + return epochStart + uint64(time.Now().UnixNano()/100) +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} diff --git a/vendor/github.com/satori/go.uuid/sql.go b/vendor/github.com/satori/go.uuid/sql.go new file mode 100644 index 0000000..56759d3 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/sql.go @@ -0,0 +1,78 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Value implements the driver.Valuer interface. +func (u UUID) Value() (driver.Value, error) { + return u.String(), nil +} + +// Scan implements the sql.Scanner interface. +// A 16-byte slice is handled by UnmarshalBinary, while +// a longer byte slice or a string is handled by UnmarshalText. +func (u *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + if len(src) == Size { + return u.UnmarshalBinary(src) + } + return u.UnmarshalText(src) + + case string: + return u.UnmarshalText([]byte(src)) + } + + return fmt.Errorf("uuid: cannot convert %T to UUID", src) +} + +// NullUUID can be used with the standard sql package to represent a +// UUID value that can be NULL in the database +type NullUUID struct { + UUID UUID + Valid bool +} + +// Value implements the driver.Valuer interface. +func (u NullUUID) Value() (driver.Value, error) { + if !u.Valid { + return nil, nil + } + // Delegate to UUID Value function + return u.UUID.Value() +} + +// Scan implements the sql.Scanner interface. +func (u *NullUUID) Scan(src interface{}) error { + if src == nil { + u.UUID, u.Valid = Nil, false + return nil + } + + // Delegate to UUID Scan function + u.Valid = true + return u.UUID.Scan(src) +} diff --git a/vendor/github.com/satori/go.uuid/uuid.go b/vendor/github.com/satori/go.uuid/uuid.go new file mode 100644 index 0000000..a2b8e2c --- /dev/null +++ b/vendor/github.com/satori/go.uuid/uuid.go @@ -0,0 +1,161 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Package uuid provides implementation of Universally Unique Identifier (UUID). +// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and +// version 2 (as specified in DCE 1.1). +package uuid + +import ( + "bytes" + "encoding/hex" +) + +// Size of a UUID in bytes. +const Size = 16 + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [Size]byte + +// UUID versions +const ( + _ byte = iota + V1 + V2 + V3 + V4 + V5 +) + +// UUID layout variants. +const ( + VariantNCS byte = iota + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// UUID DCE domains. +const ( + DomainPerson = iota + DomainGroup + DomainOrg +) + +// String parse helpers. +var ( + urnPrefix = []byte("urn:uuid:") + byteGroups = []int{8, 4, 4, 4, 12} +) + +// Nil is special form of UUID that is specified to have all +// 128 bits set to zero. +var Nil = UUID{} + +// Predefined namespace UUIDs. +var ( + NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) +) + +// Equal returns true if u1 and u2 equals, otherwise returns false. +func Equal(u1 UUID, u2 UUID) bool { + return bytes.Equal(u1[:], u2[:]) +} + +// Version returns algorithm version used to generate UUID. +func (u UUID) Version() byte { + return u[6] >> 4 +} + +// Variant returns UUID layout variant. +func (u UUID) Variant() byte { + switch { + case (u[8] >> 7) == 0x00: + return VariantNCS + case (u[8] >> 6) == 0x02: + return VariantRFC4122 + case (u[8] >> 5) == 0x06: + return VariantMicrosoft + case (u[8] >> 5) == 0x07: + fallthrough + default: + return VariantFuture + } +} + +// Bytes returns bytes slice representation of UUID. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Returns canonical string representation of UUID: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = '-' + hex.Encode(buf[9:13], u[4:6]) + buf[13] = '-' + hex.Encode(buf[14:18], u[6:8]) + buf[18] = '-' + hex.Encode(buf[19:23], u[8:10]) + buf[23] = '-' + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} + +// SetVersion sets version bits. +func (u *UUID) SetVersion(v byte) { + u[6] = (u[6] & 0x0f) | (v << 4) +} + +// SetVariant sets variant bits. +func (u *UUID) SetVariant(v byte) { + switch v { + case VariantNCS: + u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) + case VariantRFC4122: + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + case VariantMicrosoft: + u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) + case VariantFuture: + fallthrough + default: + u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) + } +} + +// Must is a helper that wraps a call to a function returning (UUID, error) +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")); +func Must(u UUID, err error) UUID { + if err != nil { + panic(err) + } + return u +}