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.
# 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
- **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

View File

@ -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**

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)
- [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

View File

@ -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:

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 (
"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).

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)
}
}
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")

View File

@ -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)

View File

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

View File

@ -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()

2
doc.go
View File

@ -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

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)
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).