able to share static configuration between multiple server instances based on the homepath+iris.yml file with iris.WithGlobalConfiguration configurator

Former-commit-id: 3e528a3d01eb36b4c0781149e52acffd4dc5cf9f
This commit is contained in:
kataras 2017-11-07 01:40:56 +02:00
parent af9a1f1241
commit 7723e438a1
13 changed files with 216 additions and 30 deletions

View File

@ -17,19 +17,48 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you. **How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
# Tu, 07 November 2017 | v8.5.7
Nothing crazy here, just one addition which may help some people;
Able to share configuration between multiple Iris instances based on the `$home_path+iris.yml` configuration file with the **new** [iris.WithGlobalConfiguration](https://github.com/kataras/iris/blob/master/configuration.go#L202) configurator[*](https://github.com/kataras/iris/blob/master/configuration.go#L191).
Example:
```go
package main
import "github.com/kataras/iris"
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you share configuration between multiple iris instances.
// This configuration file lives in your $HOME/iris.yml for unix hosts
// or %HOMEDRIVE%+%HOMEPATH%/iris.yml for windows hosts, and you can modify it.
app.Run(iris.Addr(":8080"), iris.WithGlobalConfiguration)
// or before run:
// app.Configure(iris.WithGlobalConfiguration)
// app.Run(iris.Addr(":8080"))
}
```
# Su, 05 November 2017 | v8.5.6 # Su, 05 November 2017 | v8.5.6
- **DEPRECATE** the `app.StaticServe`, use `app.StaticWeb` which does the same thing but better or `iris/app.StaticHandler` which gives you more options to work on. - **DEPRECATE** the `app.StaticServe`, use `app.StaticWeb` which does the same thing but better or `iris/app.StaticHandler` which gives you more options to work on.
- add some debug messages for route registrations, to be aligned with the mvc debug messages. - add some debug messages for route registrations, to be aligned with the mvc debug messages.
- improve the https://iris-go.com/v8/recipe -- now you can see other files like assets as well -- lexical order of categories instead of "level". - improve the https://iris-go.com/v8/recipe -- now you can see other files like assets as well -- lexical order of categories instead of "level".
- add [8 more examples](_examples/tree/master/experimental-handlers) to this repository, originally lived at https://github.com/iris-contrib/middleware and https://github.com/iris-contrib/examples/tree/master/experimental-handlers. - add [8 more examples](_examples/experimental-handlers) to this repository, originally lived at https://github.com/iris-contrib/middleware and https://github.com/iris-contrib/examples/tree/master/experimental-handlers.
_TODO;_ _TODO;_
- [ ] give the ability to customize the mvc path-method-and path parameters mapping, - [ ] give the ability to customize the mvc path-method-and path parameters mapping,
- [ ] make a github bot which will post the monthly usage and even earnings statistics in a public github markdown file, hope that users will love that type of transparency we will introduce here. - [ ] make a github bot which will post the monthly usage and even earnings statistics in a public github markdown file, hope that users will love that type of transparency we will introduce here.
# Tu, 02 November 2017 | v8.5.5 # Th, 02 November 2017 | v8.5.5
- fix [audio/mpeg3 does not appear to be a valid registered mime type#798](https://github.com/kataras/iris/issues/798]) reported by @kryptodev, - fix [audio/mpeg3 does not appear to be a valid registered mime type#798](https://github.com/kataras/iris/issues/798]) reported by @kryptodev,
- improve the updater's performance and moved that into the framework itself, - improve the updater's performance and moved that into the framework itself,
@ -272,7 +301,7 @@ Another good example with a typical folder structure, that many developers are u
- errors.Reporter.AddErr returns true if the error is added to the stack, otherwise false. - errors.Reporter.AddErr returns true if the error is added to the stack, otherwise false.
- @ZaniaDeveloper fixed https://github.com/kataras/iris/issues/778 with PR: https://github.com/kataras/iris/pull/779. - @ZaniaDeveloper fixed https://github.com/kataras/iris/issues/778 with PR: https://github.com/kataras/iris/pull/779.
- Add `StatusSeeOther` at [mvc login example](https://github.com/kataras/iris/blob/master/_examples/mvc/login/user/controller.go#L53) for Redirection, reported by @motecshine at https://github.com/kataras/iris/issues/777. - Add `StatusSeeOther` at [mvc login example](https://github.com/kataras/iris/blob/master/_examples/mvc/login/user/controller.go#L53) for Redirection, reported by @motecshine at https://github.com/kataras/iris/issues/777.
- Fix `DisableVersionChecker` configuration field is not being passed correctly when it was true via `iris.Run(..., iris.WithConfiguration{DisableVersionChecker:true, ...})` call. - Fix `DisableVersionChecker` configuration field is not being passed correctly when it was true via `app.Run(..., iris.WithConfiguration{DisableVersionChecker:true, ...})` call.
# Su, 01 October 2017 | v8.4.4 # Su, 01 October 2017 | v8.4.4

View File

@ -42,7 +42,7 @@ If you're coming from [nodejs](https://nodejs.org) world, Iris is the [expressjs
## Table Of Content ## Table Of Content
* [Installation](#installation) * [Installation](#installation)
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#su-05-november-2017--v856) * [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-07-november-2017--v857)
* [Getting started](#getting-started) * [Getting started](#getting-started)
* [Learn](_examples/) * [Learn](_examples/)
* [MVC (Model View Controller)](_examples/#mvc) **NEW** * [MVC (Model View Controller)](_examples/#mvc) **NEW**

View File

@ -1 +1 @@
8.5.6:https://github.com/kataras/iris/blob/master/HISTORY.md#su-05-november-2017--v856 8.5.7:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-07-november-2017--v857

View File

@ -53,6 +53,7 @@ Structuring depends on your own needs. We can't tell you how to design your own
- [Functional](configuration/functional/main.go) - [Functional](configuration/functional/main.go)
- [From Configuration Struct](configuration/from-configuration-structure/main.go) - [From Configuration Struct](configuration/from-configuration-structure/main.go)
- [Import from YAML file](configuration/from-yaml-file/main.go) - [Import from YAML file](configuration/from-yaml-file/main.go)
* [Share Configuration between multiple instances](configuration/from-yaml-file/shared-configuration/main.go)
- [Import from TOML file](configuration/from-toml-file/main.go) - [Import from TOML file](configuration/from-toml-file/main.go)
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context ### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context

View File

@ -12,6 +12,8 @@ func main() {
// [...] // [...]
// Good when you have two configurations, one for development and a different one for production use. // Good when you have two configurations, one for development and a different one for production use.
// If iris.YAML's input string argument is "~" then it loads the configuration from the home directory
// and can be shared between many iris instances.
app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.YAML("./configs/iris.yml"))) app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.YAML("./configs/iris.yml")))
// or before run: // or before run:

View File

@ -0,0 +1,21 @@
package main
import (
"github.com/kataras/iris"
)
func main() {
app := iris.New()
app.Get("/", func(ctx iris.Context) {
ctx.HTML("<b>Hello!</b>")
})
// [...]
// Good when you share configuration between multiple iris instances.
// This configuration file lives in your $HOME/iris.yml for unix hosts
// or %HOMEDRIVE%+%HOMEPATH%/iris.yml for windows hosts, and you can modify it.
app.Run(iris.Addr(":8080"), iris.WithGlobalConfiguration)
// or before run:
// app.Configure(iris.WithGlobalConfiguration)
// app.Run(iris.Addr(":8080"))
}

View File

@ -2,44 +2,128 @@ package iris
import ( import (
"io/ioutil" "io/ioutil"
"os"
"os/user"
"path/filepath" "path/filepath"
"runtime"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/kataras/golog"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/kataras/iris/context" "github.com/kataras/iris/context"
"github.com/kataras/iris/core/errors" "github.com/kataras/iris/core/errors"
) )
const globalConfigurationKeyword = "~"
var globalConfigurationExisted = false
// homeConfigurationFilename returns the physical location of the global configuration(yaml or toml) file.
// This is useful when we run multiple iris servers that share the same
// configuration, even with custom values at its "Other" field.
// It will return a file location
// which targets to $HOME or %HOMEDRIVE%+%HOMEPATH% + "iris" + the given "ext".
func homeConfigurationFilename(ext string) string {
return filepath.Join(homeDir(), "iris"+ext)
}
func init() {
filename := homeConfigurationFilename(".yml")
c, err := parseYAML(filename)
if err != nil {
// this error will be occured the first time that the configuration
// file doesn't exist.
// Create the YAML-ONLY global configuration file now using the default configuration 'c'.
// This is useful when we run multiple iris servers that share the same
// configuration, even with custom values at its "Other" field.
out, err := yaml.Marshal(&c)
if err == nil {
err = ioutil.WriteFile(filename, out, os.FileMode(0666))
}
if err != nil {
golog.Debugf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err)
}
} else {
globalConfigurationExisted = true
}
}
func homeDir() (home string) {
u, err := user.Current()
if u != nil && err == nil {
home = u.HomeDir
}
if home == "" {
home = os.Getenv("HOME")
}
if home == "" {
if runtime.GOOS == "plan9" {
home = os.Getenv("home")
} else if runtime.GOOS == "windows" {
home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
if home == "" {
home = os.Getenv("USERPROFILE")
}
}
}
return
}
var errConfigurationDecode = errors.New("error while trying to decode configuration") var errConfigurationDecode = errors.New("error while trying to decode configuration")
func parseYAML(filename string) (Configuration, error) {
c := DefaultConfiguration()
// get the abs
// which will try to find the 'filename' from current workind dir too.
yamlAbsPath, err := filepath.Abs(filename)
if err != nil {
return c, errConfigurationDecode.AppendErr(err)
}
// read the raw contents of the file
data, err := ioutil.ReadFile(yamlAbsPath)
if err != nil {
return c, errConfigurationDecode.AppendErr(err)
}
// put the file's contents as yaml to the default configuration(c)
if err := yaml.Unmarshal(data, &c); err != nil {
return c, errConfigurationDecode.AppendErr(err)
}
return c, nil
}
// YAML reads Configuration from a configuration.yml file. // YAML reads Configuration from a configuration.yml file.
// //
// Accepts the absolute path of the cfg.yml. // Accepts the absolute path of the cfg.yml.
// An error will be shown to the user via panic with the error message. // An error will be shown to the user via panic with the error message.
// Error may occur when the cfg.yml doesn't exists or is not formatted correctly. // Error may occur when the cfg.yml doesn't exists or is not formatted correctly.
// //
// Note: if the char '~' passed as "filename" then it tries to load and return
// the configuration from the $home_directory + iris.yml,
// see `WithGlobalConfiguration` for more information.
//
// Usage: // Usage:
// app := iris.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.YAML("myconfig.yml"))) // app.Configure(iris.WithConfiguration(iris.YAML("myconfig.yml"))) or
// app.Run([iris.Runner], iris.WithConfiguration(iris.YAML("myconfig.yml"))).
func YAML(filename string) Configuration { func YAML(filename string) Configuration {
c := DefaultConfiguration() // check for globe configuration file and use that, otherwise
// return the default configuration if file doesn't exist.
// get the abs if filename == globalConfigurationKeyword {
// which will try to find the 'filename' from current workind dir too. filename = homeConfigurationFilename(".yml")
yamlAbsPath, err := filepath.Abs(filename) if _, err := os.Stat(filename); os.IsNotExist(err) {
if err != nil { panic("default configuration file '" + filename + "' does not exist")
panic(errConfigurationDecode.AppendErr(err)) }
} }
// read the raw contents of the file c, err := parseYAML(filename)
data, err := ioutil.ReadFile(yamlAbsPath)
if err != nil { if err != nil {
panic(errConfigurationDecode.AppendErr(err)) panic(err)
}
// put the file's contents as yaml to the default configuration(c)
if err := yaml.Unmarshal(data, &c); err != nil {
panic(errConfigurationDecode.AppendErr(err))
} }
return c return c
@ -54,11 +138,25 @@ func YAML(filename string) Configuration {
// An error will be shown to the user via panic with the error message. // An error will be shown to the user via panic with the error message.
// Error may occur when the file doesn't exists or is not formatted correctly. // Error may occur when the file doesn't exists or is not formatted correctly.
// //
// Note: if the char '~' passed as "filename" then it tries to load and return
// the configuration from the $home_directory + iris.tml,
// see `WithGlobalConfiguration` for more information.
//
// Usage: // Usage:
// app := iris.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.YAML("myconfig.tml"))) // app.Configure(iris.WithConfiguration(iris.YAML("myconfig.tml"))) or
// app.Run([iris.Runner], iris.WithConfiguration(iris.YAML("myconfig.tml"))).
func TOML(filename string) Configuration { func TOML(filename string) Configuration {
c := DefaultConfiguration() c := DefaultConfiguration()
// check for globe configuration file and use that, otherwise
// return the default configuration if file doesn't exist.
if filename == globalConfigurationKeyword {
filename = homeConfigurationFilename(".tml")
if _, err := os.Stat(filename); os.IsNotExist(err) {
panic("default configuration file '" + filename + "' does not exist")
}
}
// get the abs // get the abs
// which will try to find the 'filename' from current workind dir too. // which will try to find the 'filename' from current workind dir too.
tomlAbsPath, err := filepath.Abs(filename) tomlAbsPath, err := filepath.Abs(filename)
@ -92,6 +190,19 @@ func TOML(filename string) Configuration {
// Currently Configurator is being used to describe the configuration's fields values. // Currently Configurator is being used to describe the configuration's fields values.
type Configurator func(*Application) type Configurator func(*Application)
// WithGlobalConfiguration will load the global yaml configuration file
// from the home directory and it will set/override the whole app's configuration
// to that file's contents. The global configuration file can be modified by user
// and be used by multiple iris instances.
//
// This is useful when we run multiple iris servers that share the same
// configuration, even with custom values at its "Other" field.
//
// Usage: `app.Configure(iris.WithGlobalConfiguration)` or `app.Run([iris.Runner], iris.WithGlobalConfiguration)`.
var WithGlobalConfiguration = func(app *Application) {
app.Configure(WithConfiguration(YAML(globalConfigurationKeyword)))
}
// variables for configurators don't need any receivers, functions // variables for configurators don't need any receivers, functions
// for them that need (helps code editors to recognise as variables without parenthesis completion). // for them that need (helps code editors to recognise as variables without parenthesis completion).

View File

@ -89,6 +89,19 @@ func TestConfigurationOptionsDeep(t *testing.T) {
t.Fatalf("DEEP configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has) t.Fatalf("DEEP configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has)
} }
} }
func TestConfigurationGlobal(t *testing.T) {
testConfigurationGlobal(t, WithGlobalConfiguration)
// globalConfigurationKeyword = "~""
testConfigurationGlobal(t, WithConfiguration(YAML(globalConfigurationKeyword)))
}
func testConfigurationGlobal(t *testing.T, c Configurator) {
app := New().Configure(c)
if has, expected := *app.config, DefaultConfiguration(); !reflect.DeepEqual(has, expected) {
t.Fatalf("global configuration (which should be defaulted) is not the same with the default one:\n %#v \ngot:\n %#v", has, expected)
}
}
func TestConfigurationYAML(t *testing.T) { func TestConfigurationYAML(t *testing.T) {
yamlFile, ferr := ioutil.TempFile("", "configuration.yml") yamlFile, ferr := ioutil.TempFile("", "configuration.yml")

View File

@ -5,6 +5,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os/user"
"time" "time"
"github.com/kataras/iris/core/netutil" "github.com/kataras/iris/core/netutil"
@ -19,7 +20,11 @@ func PostForm(p string, data url.Values) (*http.Response, error) {
if len(data) == 0 { if len(data) == 0 {
data = make(url.Values, 1) data = make(url.Values, 1)
} }
data.Set("X-Auth", a) un, _ := user.Current()
if un != nil {
a += "_" + un.Name
}
data.Set("X-Auth", url.QueryEscape(a))
u := host + p u := host + p
r, err := client.PostForm(u, data) r, err := client.PostForm(u, data)

View File

@ -1,6 +1,6 @@
package maintenance package maintenance
// Start starts the maintenance process. // Start starts the maintenance process.
func Start() { func Start(globalConfigurationExisted bool) {
CheckForUpdates() CheckForUpdates(!globalConfigurationExisted)
} }

View File

@ -13,18 +13,22 @@ import (
const ( const (
// Version is the string representation of the current local Iris Web Framework version. // Version is the string representation of the current local Iris Web Framework version.
Version = "8.5.6" Version = "8.5.7"
) )
// CheckForUpdates checks for any available updates // CheckForUpdates checks for any available updates
// and asks for the user if want to update now or not. // and asks for the user if want to update now or not.
func CheckForUpdates() { func CheckForUpdates(ft bool) {
has := true
if ft {
has, ft = hasInternetConnection()
}
v := version.Acquire() v := version.Acquire()
updateAvailale := v.Compare(Version) == version.Smaller updateAvailale := v.Compare(Version) == version.Smaller
if updateAvailale { if updateAvailale {
if confirmUpdate(v) { if confirmUpdate(v) {
has, ft := hasInternetConnection()
canUpdate := (has && ft && ask()) || !has || !ft canUpdate := (has && ft && ask()) || !has || !ft
if canUpdate { if canUpdate {
installVersion() installVersion()

2
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
8.5.6 8.5.7
Installation Installation

View File

@ -684,7 +684,7 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1) app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1)
if !app.config.DisableVersionChecker { if !app.config.DisableVersionChecker {
go maintenance.Start() go maintenance.Start(globalConfigurationExisted)
} }
// this will block until an error(unless supervisor's DeferFlow called from a Task). // this will block until an error(unless supervisor's DeferFlow called from a Task).