diff --git a/HISTORY.md b/HISTORY.md
index e3d14ac9..12a13b12 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -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.
+# 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("Hello!")
+ })
+ // [...]
+
+ // 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
- **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.
- 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;_
- [ ] 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.
-# 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,
- 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.
- @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.
-- 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
diff --git a/README.md b/README.md
index 86242417..884adad7 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ If you're coming from [nodejs](https://nodejs.org) world, Iris is the [expressjs
## Table Of Content
* [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)
* [Learn](_examples/)
* [MVC (Model View Controller)](_examples/#mvc) **NEW**
diff --git a/VERSION b/VERSION
index dda843d0..60a5aaea 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.5.6:https://github.com/kataras/iris/blob/master/HISTORY.md#su-05-november-2017--v856
\ No newline at end of file
+8.5.7:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-07-november-2017--v857
\ No newline at end of file
diff --git a/_examples/README.md b/_examples/README.md
index c5578eea..4a23bed3 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -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)
- [From Configuration Struct](configuration/from-configuration-structure/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)
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context
diff --git a/_examples/configuration/from-yaml-file/main.go b/_examples/configuration/from-yaml-file/main.go
index 679b0d76..ad42e74b 100644
--- a/_examples/configuration/from-yaml-file/main.go
+++ b/_examples/configuration/from-yaml-file/main.go
@@ -12,6 +12,8 @@ func main() {
// [...]
// 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")))
// or before run:
diff --git a/_examples/configuration/from-yaml-file/shared-configuration/main.go b/_examples/configuration/from-yaml-file/shared-configuration/main.go
new file mode 100644
index 00000000..25159af3
--- /dev/null
+++ b/_examples/configuration/from-yaml-file/shared-configuration/main.go
@@ -0,0 +1,21 @@
+package main
+
+import (
+ "github.com/kataras/iris"
+)
+
+func main() {
+ app := iris.New()
+ app.Get("/", func(ctx iris.Context) {
+ ctx.HTML("Hello!")
+ })
+ // [...]
+
+ // 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"))
+}
diff --git a/configuration.go b/configuration.go
index 5a2ed94a..a171ae3e 100644
--- a/configuration.go
+++ b/configuration.go
@@ -2,44 +2,128 @@ package iris
import (
"io/ioutil"
+ "os"
+ "os/user"
"path/filepath"
+ "runtime"
"github.com/BurntSushi/toml"
+ "github.com/kataras/golog"
"gopkg.in/yaml.v2"
"github.com/kataras/iris/context"
"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")
+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.
//
// Accepts the absolute path of the cfg.yml.
// 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.
//
+// 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:
-// 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 {
- 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 {
- panic(errConfigurationDecode.AppendErr(err))
+ // check for globe configuration file and use that, otherwise
+ // return the default configuration if file doesn't exist.
+ if filename == globalConfigurationKeyword {
+ filename = homeConfigurationFilename(".yml")
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
+ panic("default configuration file '" + filename + "' does not exist")
+ }
}
- // read the raw contents of the file
- data, err := ioutil.ReadFile(yamlAbsPath)
+ c, err := parseYAML(filename)
if err != nil {
- panic(errConfigurationDecode.AppendErr(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))
+ panic(err)
}
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.
// 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:
-// 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 {
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
// which will try to find the 'filename' from current workind dir too.
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.
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
// for them that need (helps code editors to recognise as variables without parenthesis completion).
diff --git a/configuration_test.go b/configuration_test.go
index c3ae4e38..f5aed4f7 100644
--- a/configuration_test.go
+++ b/configuration_test.go
@@ -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)
}
}
+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) {
yamlFile, ferr := ioutil.TempFile("", "configuration.yml")
diff --git a/core/maintenance/client/client.go b/core/maintenance/client/client.go
index d10431ef..382ade84 100644
--- a/core/maintenance/client/client.go
+++ b/core/maintenance/client/client.go
@@ -5,6 +5,7 @@ import (
"net"
"net/http"
"net/url"
+ "os/user"
"time"
"github.com/kataras/iris/core/netutil"
@@ -19,7 +20,11 @@ func PostForm(p string, data url.Values) (*http.Response, error) {
if len(data) == 0 {
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
r, err := client.PostForm(u, data)
diff --git a/core/maintenance/maintenance.go b/core/maintenance/maintenance.go
index 613b67fd..4d098ab0 100644
--- a/core/maintenance/maintenance.go
+++ b/core/maintenance/maintenance.go
@@ -1,6 +1,6 @@
package maintenance
// Start starts the maintenance process.
-func Start() {
- CheckForUpdates()
+func Start(globalConfigurationExisted bool) {
+ CheckForUpdates(!globalConfigurationExisted)
}
diff --git a/core/maintenance/version.go b/core/maintenance/version.go
index 01faf308..a5e019c8 100644
--- a/core/maintenance/version.go
+++ b/core/maintenance/version.go
@@ -13,18 +13,22 @@ import (
const (
// 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
// 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()
updateAvailale := v.Compare(Version) == version.Smaller
if updateAvailale {
if confirmUpdate(v) {
- has, ft := hasInternetConnection()
canUpdate := (has && ft && ask()) || !has || !ft
if canUpdate {
installVersion()
diff --git a/doc.go b/doc.go
index 799d8373..c00e752b 100644
--- a/doc.go
+++ b/doc.go
@@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version
-8.5.6
+8.5.7
Installation
diff --git a/iris.go b/iris.go
index 7569f61c..29853228 100644
--- a/iris.go
+++ b/iris.go
@@ -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)
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).