aboutsummaryrefslogtreecommitdiff
path: root/internal/environment
diff options
context:
space:
mode:
Diffstat (limited to 'internal/environment')
-rw-r--r--internal/environment/environment.go173
-rw-r--r--internal/environment/environment_test.go61
-rw-r--r--internal/environment/flags.go57
3 files changed, 291 insertions, 0 deletions
diff --git a/internal/environment/environment.go b/internal/environment/environment.go
new file mode 100644
index 0000000..eac0430
--- /dev/null
+++ b/internal/environment/environment.go
@@ -0,0 +1,173 @@
+// Copyright 2018 ThousandEyes Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package environment
+
+import (
+ "fmt"
+ "html/template"
+ "io/ioutil"
+ "net"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "sync"
+
+ "github.com/thousandeyes/shoelaces/internal/event"
+ "github.com/thousandeyes/shoelaces/internal/log"
+ "github.com/thousandeyes/shoelaces/internal/mappings"
+ "github.com/thousandeyes/shoelaces/internal/server"
+ "github.com/thousandeyes/shoelaces/internal/templates"
+)
+
+// Environment struct holds the shoelaces instance global data.
+type Environment struct {
+ ConfigFile string
+ BaseURL string
+ HostnameMaps []mappings.HostnameMap
+ NetworkMaps []mappings.NetworkMap
+ ServerStates *server.States
+ EventLog *event.Log
+ ParamsBlacklist []string
+ Templates *templates.ShoelacesTemplates // Dynamic slc templates
+ StaticTemplates *template.Template // Static Templates
+ Environments []string // Valid config environments
+ Logger log.Logger
+
+ Port int
+ Domain string
+ DataDir string
+ StaticDir string
+ EnvDir string
+ TemplateExtension string
+ MappingsFile string
+ Debug bool
+}
+
+// New returns an initialized environment structure
+func New() *Environment {
+ env := defaultEnvironment()
+ env.setFlags()
+ env.validateFlags()
+
+ if env.Debug {
+ env.Logger = log.AllowDebug(env.Logger)
+ }
+
+ env.BaseURL = fmt.Sprintf("%s:%d", env.Domain, env.Port)
+ env.Environments = env.initEnvOverrides()
+
+ env.EventLog = &event.Log{}
+
+ env.Logger.Info("component", "environment", "msg", "Override found", "environment", env.Environments)
+
+ mappingsPath := path.Join(env.DataDir, env.MappingsFile)
+ if err := env.initMappings(mappingsPath); err != nil {
+ panic(err)
+ }
+
+ env.initStaticTemplates()
+ env.Templates.ParseTemplates(env.Logger, env.DataDir, env.EnvDir, env.Environments, env.TemplateExtension)
+ server.StartStateCleaner(env.Logger, env.ServerStates)
+
+ return env
+}
+
+func defaultEnvironment() *Environment {
+ env := &Environment{}
+ env.NetworkMaps = make([]mappings.NetworkMap, 0)
+ env.HostnameMaps = make([]mappings.HostnameMap, 0)
+ env.ServerStates = &server.States{sync.RWMutex{}, make(map[string]*server.State)}
+ env.ParamsBlacklist = []string{"baseURL"}
+ env.Templates = templates.New()
+ env.Environments = make([]string, 0)
+ env.Logger = log.MakeLogger(os.Stdout)
+
+ return env
+}
+
+func (env *Environment) initStaticTemplates() {
+ staticTemplates := []string{
+ path.Join(env.StaticDir, "templates/html/header.html"),
+ path.Join(env.StaticDir, "templates/html/index.html"),
+ path.Join(env.StaticDir, "templates/html/events.html"),
+ path.Join(env.StaticDir, "templates/html/mappings.html"),
+ path.Join(env.StaticDir, "templates/html/footer.html"),
+ }
+
+ fmt.Println(env.StaticDir)
+
+ for _, t := range staticTemplates {
+ if _, err := os.Stat(t); err != nil {
+ env.Logger.Error("component", "environment", "msg", "Template does not exists!", "environment", t)
+ os.Exit(1)
+ }
+ }
+
+ env.StaticTemplates = template.Must(template.ParseFiles(staticTemplates...))
+}
+
+func (env *Environment) initEnvOverrides() []string {
+ var environments = make([]string, 0)
+ envPath := filepath.Join(env.DataDir, env.EnvDir)
+ files, err := ioutil.ReadDir(envPath)
+ if err == nil {
+ for _, f := range files {
+ if f.IsDir() {
+ environments = append(environments, f.Name())
+ }
+ }
+ }
+ return environments
+}
+
+func (env *Environment) initMappings(mappingsPath string) error {
+ configMappings := mappings.ParseYamlMappings(env.Logger, mappingsPath)
+
+ for _, configNetMap := range configMappings.NetworkMaps {
+ _, ipnet, err := net.ParseCIDR(configNetMap.Network)
+ if err != nil {
+ return err
+ }
+
+ netMap := mappings.NetworkMap{Network: ipnet, Script: initScript(configNetMap.Script)}
+ env.NetworkMaps = append(env.NetworkMaps, netMap)
+ }
+
+ for _, configHostMap := range configMappings.HostnameMaps {
+ regex, err := regexp.Compile(configHostMap.Hostname)
+ if err != nil {
+ return err
+ }
+
+ hostMap := mappings.HostnameMap{Hostname: regex, Script: initScript(configHostMap.Script)}
+ env.HostnameMaps = append(env.HostnameMaps, hostMap)
+ }
+
+ return nil
+}
+
+func initScript(configScript mappings.YamlScript) *mappings.Script {
+ mappingScript := &mappings.Script{
+ Name: configScript.Name,
+ Environment: configScript.Environment,
+ Params: make(map[string]interface{}),
+ }
+ for key := range configScript.Params {
+ mappingScript.Params[key] = configScript.Params[key]
+ }
+
+ return mappingScript
+}
diff --git a/internal/environment/environment_test.go b/internal/environment/environment_test.go
new file mode 100644
index 0000000..8ffe88d
--- /dev/null
+++ b/internal/environment/environment_test.go
@@ -0,0 +1,61 @@
+// Copyright 2018 ThousandEyes Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package environment
+
+import (
+ "testing"
+
+ "github.com/thousandeyes/shoelaces/internal/mappings"
+)
+
+func TestDefaultEnvironment(t *testing.T) {
+ env := defaultEnvironment()
+ if env.BaseURL != "" {
+ t.Error("BaseURL should be empty string if instantiated directly.")
+ }
+ if len(env.HostnameMaps) != 0 {
+ t.Error("Hostname mappings should be empty")
+ }
+ if len(env.NetworkMaps) != 0 {
+ t.Error("Network mappings should be empty")
+ }
+ if len(env.ParamsBlacklist) != 1 &&
+ env.ParamsBlacklist[0] != "baseURL" {
+ t.Error("ParamsBlacklist should have only baseURL")
+ }
+}
+
+func TestInitScript(t *testing.T) {
+ params := make(map[string]string)
+ params["one"] = "one_value"
+ configScript := mappings.YamlScript{Name: "testscript", Params: params}
+ mappingScript := initScript(configScript)
+ if mappingScript.Name != "testscript" {
+ t.Errorf("Expected: %s\nGot: %s\n", "testscript", mappingScript.Name)
+ }
+ val, ok := mappingScript.Params["one"]
+ if !ok {
+ t.Error("Missing param")
+ } else {
+ v, ok := val.(string)
+ if !ok {
+ t.Error("Bad value type")
+ } else {
+ if v != "one_value" {
+ t.Error("Bad value")
+ }
+ }
+ }
+}
diff --git a/internal/environment/flags.go b/internal/environment/flags.go
new file mode 100644
index 0000000..8250690
--- /dev/null
+++ b/internal/environment/flags.go
@@ -0,0 +1,57 @@
+// Copyright 2018 ThousandEyes Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package environment
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/namsral/flag"
+)
+
+func (env *Environment) setFlags() {
+ flag.StringVar(&env.ConfigFile, "config", "", "My config file")
+ flag.IntVar(&env.Port, "port", 8080, "The port where I'm going to listen")
+ flag.StringVar(&env.Domain, "domain", "localhost", "The address where I'm going to listen")
+ flag.StringVar(&env.DataDir, "data-dir", "", "Directory with mappings, configs, templates, etc.")
+ flag.StringVar(&env.StaticDir, "static-dir", "web", "A custom web directory with static files")
+ flag.StringVar(&env.EnvDir, "env-dir", "env_overrides", "Directory with overrides")
+ flag.StringVar(&env.TemplateExtension, "template-extension", ".slc", "Shoelaces template extension")
+ flag.StringVar(&env.MappingsFile, "mappings-file", "mappings.yaml", "My mappings YAML file")
+ flag.BoolVar(&env.Debug, "debug", false, "Debug mode")
+
+ flag.Parse()
+}
+
+func (env *Environment) validateFlags() {
+ error := false
+
+ if env.DataDir == "" {
+ fmt.Println("[*] You must specify the data-dir parameter")
+ error = true
+ }
+
+ if env.StaticDir == "" {
+ fmt.Println("[*] You must specify the data-dir parameter")
+ error = true
+ }
+
+ if error {
+ fmt.Println("\nAvailable parameters:")
+ flag.PrintDefaults()
+ fmt.Println("\nParameters can be specified as environment variables, arguments or in a config file.")
+ os.Exit(1)
+ }
+}
nihil fit ex nihilo