diff options
Diffstat (limited to 'internal/mappings')
-rw-r--r-- | internal/mappings/mappings.go | 80 | ||||
-rw-r--r-- | internal/mappings/mappings_test.go | 100 | ||||
-rw-r--r-- | internal/mappings/parse.go | 78 |
3 files changed, 258 insertions, 0 deletions
diff --git a/internal/mappings/mappings.go b/internal/mappings/mappings.go new file mode 100644 index 0000000..fba7201 --- /dev/null +++ b/internal/mappings/mappings.go @@ -0,0 +1,80 @@ +// 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 mappings + +import ( + "net" + "regexp" + "strings" +) + +// Script holds information related to a booting script. +type Script struct { + Name string + Environment string + Params map[string]interface{} +} + +// NetworkMap struct contains an association between a CIDR network and a +// Script. +type NetworkMap struct { + Network *net.IPNet + Script *Script +} + +// HostnameMap struct contains an association between a hostname regular +// expression and a Script. +type HostnameMap struct { + Hostname *regexp.Regexp + Script *Script +} + +// FindScriptForHostname receives a HostnameMap and a string (that can be a +// regular expression), and tries to find a match in that map. If it finds +// a match, it returns the associated script. +func FindScriptForHostname(maps []HostnameMap, hostname string) (script *Script, ok bool) { + for _, m := range maps { + if m.Hostname.MatchString(hostname) { + return m.Script, true + } + } + return nil, false +} + +// FindScriptForNetwork receives a NetworkMap and an IP and tries to see if +// that IP belongs to any of the configured networks. If it finds a match, +// it returns the associated script. +func FindScriptForNetwork(maps []NetworkMap, ip string) (script *Script, ok bool) { + for _, m := range maps { + if m.Network.Contains(net.ParseIP(ip)) { + return m.Script, true + } + } + return nil, false +} + +func (s Script) String() string { + var result = s.Name + " : { " + elems := []string{} + if s.Environment != "" { + elems = append(elems, "environment: "+s.Environment) + } + for key, value := range s.Params { + elems = append(elems, key+": "+value.(string)) + } + result += strings.Join(elems, ", ") + " }" + + return result +} diff --git a/internal/mappings/mappings_test.go b/internal/mappings/mappings_test.go new file mode 100644 index 0000000..ed45114 --- /dev/null +++ b/internal/mappings/mappings_test.go @@ -0,0 +1,100 @@ +// 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 mappings + +import ( + "net" + "regexp" + "testing" +) + +var ( + mockScriptParams1 = map[string]interface{}{ + "param11": "param1_value1", + "param21": "param2_value1", + } + mockScriptParams2 = map[string]interface{}{ + "param12": "param1_value2", + "param22": "param2_value2", + } + mockScript1 = Script{Name: "mock_script1", Params: mockScriptParams1} + mockScript2 = Script{Name: "mock_script2", Params: mockScriptParams2} + + mockRegex1, _ = regexp.Compile("mock_host1") + mockRegex2, _ = regexp.Compile("mock_host2") + + mockHostNameMap1 = HostnameMap{ + Hostname: mockRegex1, + Script: &mockScript1, + } + + mockHostNameMap2 = HostnameMap{ + Hostname: mockRegex2, + Script: &mockScript2, + } + + _, mockNetwork1, _ = net.ParseCIDR("10.0.0.0/8") + _, mockNetwork2, _ = net.ParseCIDR("192.168.0.0/16") + + mockNetworkMap1 = NetworkMap{ + Network: mockNetwork1, + Script: &mockScript1, + } + mockNetworkMap2 = NetworkMap{ + Network: mockNetwork2, + Script: &mockScript2, + } +) + +func TestScript(t *testing.T) { + expected1 := "mock_script1 : { param11: param1_value1, param21: param2_value1 }" + expected2 := "mock_script1 : { param21: param2_value1, param11: param1_value1 }" + mockScriptString := mockScript1.String() + if mockScriptString != expected1 && mockScriptString != expected2 { + t.Errorf("Expected: %s or %s\nGot: %s\n", expected1, expected2, mockScriptString) + } +} + +func TestFindScriptForHostname(t *testing.T) { + maps := []HostnameMap{mockHostNameMap1, mockHostNameMap2} + script, success := FindScriptForHostname(maps, "mock_host1") + if !(script.Name == "mock_script1" && success) { + t.Error("Hostname should have matched") + } + script, success = FindScriptForHostname(maps, "mock_host2") + if !(script.Name == "mock_script2" && success) { + t.Error("Hostname should have matched") + } + script, success = FindScriptForHostname(maps, "mock_host_bad") + if !(script == nil && !success) { + t.Error("Hostname should have not matched") + } +} + +func TestScriptForNetwork(t *testing.T) { + maps := []NetworkMap{mockNetworkMap1, mockNetworkMap2} + script, success := FindScriptForNetwork(maps, "10.0.0.1") + if !(script.Name == "mock_script1" && success) { + t.Error("IP should have matched the network map") + } + script, success = FindScriptForNetwork(maps, "192.168.0.1") + if !(script.Name == "mock_script2" && success) { + t.Error("IP should have matched the network map") + } + script, success = FindScriptForNetwork(maps, "8.8.8.8") + if !(script == nil && !success) { + t.Error("IP shouildn't have matched the network map") + } +} diff --git a/internal/mappings/parse.go b/internal/mappings/parse.go new file mode 100644 index 0000000..64de5bb --- /dev/null +++ b/internal/mappings/parse.go @@ -0,0 +1,78 @@ +// 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 mappings + +import ( + "io/ioutil" + "os" + + "gopkg.in/yaml.v2" + + "github.com/thousandeyes/shoelaces/internal/log" +) + +// Mappings struct contains YamlNetworkMaps and YamlHostnameMaps. +type Mappings struct { + NetworkMaps []YamlNetworkMap `yaml:"networkMaps"` + HostnameMaps []YamlHostnameMap `yaml:"hostnameMaps"` +} + +// YamlNetworkMap struct contains an association between a CIDR network and a +// Script. It's different than mapping.NetworkMap in the sense that this +// struct can be used to parse the JSON mapping file. +type YamlNetworkMap struct { + Network string + Script YamlScript +} + +// YamlHostnameMap struct contains an association between a hostname regular +// expression and a Script. It's different than mapping.HostnameMap in the +// sense that this struct can be used to parse the JSON mapping file. +type YamlHostnameMap struct { + Hostname string + Script YamlScript +} + +// YamlScript holds information regarding a script. Its name, its environment +// and its parameters. +type YamlScript struct { + Name string + Environment string + Params map[string]string +} + +// ParseYamlMappings parses the mappings yaml file into a Mappings struct. +func ParseYamlMappings(logger log.Logger, mappingsFile string) *Mappings { + var mappings Mappings + + logger.Info("component", "config", "msg", "Reading mappings", "source", mappingsFile) + yamlFile, err := ioutil.ReadFile(mappingsFile) + + if err != nil { + logger.Error(err) + os.Exit(1) + } + + mappings.NetworkMaps = make([]YamlNetworkMap, 0) + mappings.HostnameMaps = make([]YamlHostnameMap, 0) + + err = yaml.Unmarshal(yamlFile, &mappings) + if err != nil { + logger.Error(err) + os.Exit(1) + } + + return &mappings +} |