From 9dafc22b9c5eec3eb6a2b3ada523f13b71da3c1f Mon Sep 17 00:00:00 2001 From: geauxvirtual Date: Thu, 15 Oct 2015 17:22:20 -0700 Subject: [PATCH] Remove Facter plugin. Plugin now located at https://github.com/intelsdi-x/pulse-plugin-collector-facter --- .../pulse-collector-facter/README.md | 45 ---- .../pulse-collector-facter/facter/cmd.go | 114 --------- .../pulse-collector-facter/facter/cmd_test.go | 96 -------- .../pulse-collector-facter/facter/facter.go | 219 ------------------ .../facter/facter_test.go | 192 --------------- .../collector/pulse-collector-facter/main.go | 44 ---- .../pulse-collector-facter/main_test.go | 65 ------ 7 files changed, 775 deletions(-) delete mode 100644 plugin/collector/pulse-collector-facter/README.md delete mode 100644 plugin/collector/pulse-collector-facter/facter/cmd.go delete mode 100644 plugin/collector/pulse-collector-facter/facter/cmd_test.go delete mode 100644 plugin/collector/pulse-collector-facter/facter/facter.go delete mode 100644 plugin/collector/pulse-collector-facter/facter/facter_test.go delete mode 100644 plugin/collector/pulse-collector-facter/main.go delete mode 100644 plugin/collector/pulse-collector-facter/main_test.go diff --git a/plugin/collector/pulse-collector-facter/README.md b/plugin/collector/pulse-collector-facter/README.md deleted file mode 100644 index 7dd78512a..000000000 --- a/plugin/collector/pulse-collector-facter/README.md +++ /dev/null @@ -1,45 +0,0 @@ - - -## Pulse Fact Collector Plugin - - -## Description - -Collect facts from Facter and convert them into Pulse metrics. - -Features: - -- robust -- configurable (timeout) - -## Assumptions - -- returns nothing when asked for nothing -- always return what was asked for - may return nil as value -- does not check cohesion between return metrics type GetMetricType and what is asked for - -### Entry point - -./main.go - -facter package content: - -* facter.go - implements Pulse plugin API (discover&collect) -* cmd.go - abstraction over external binary to collects fact from Facter diff --git a/plugin/collector/pulse-collector-facter/facter/cmd.go b/plugin/collector/pulse-collector-facter/facter/cmd.go deleted file mode 100644 index 8c172b6d6..000000000 --- a/plugin/collector/pulse-collector-facter/facter/cmd.go +++ /dev/null @@ -1,114 +0,0 @@ -/* -http://www.apache.org/licenses/LICENSE-2.0.txt - - -Copyright 2015 Intel Coporation - -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. -*/ - -// This modules abstracts communication with external facter program (external binary) -package facter - -import ( - "encoding/json" - "errors" - "log" - "os/exec" - "time" -) - -// helper type to deal with json parsing -// is is created by getFacts from actual values received from system -// maps a fact name to some untype value, received from Facter -type facts map[string]interface{} - -// cmdConfig stores the path to executable and defaults options to get unserializable response -type cmdConfig struct { - // where to find facter executable - // tip: somehow golang is able to find executable within PATH, so just a name is enough - // even if facter is just a ruby script - executable string - // default options passed to facter to get json output - // overriden during tests to get non parseable output - // by options I mean flags as -h -v or --json (not positional arguments like foo bar) - options []string -} - -// newDefaultCmdConfig returns default working configuration "facter --json" -func newDefaultCmdConfig() *cmdConfig { - return &cmdConfig{ - executable: "facter", - options: []string{"--json"}, - } -} - -// get facts from facter (external command) requsted by names -// returns all facts if none requested -// if cmdOptions is nil, use defaults "facter --json" -func getFacts( - names []string, - facterTimeout time.Duration, - cmdConfig *cmdConfig, -) (facts, error) { - - // nil means use defaults - if cmdConfig == nil { - cmdConfig = newDefaultCmdConfig() - } - - // copy given options into args - // > args := make([]string, len(cmdConfig.options)) - // > copy(args, cmdConfig.options) - args := append([]string{}, cmdConfig.options...) - args = append(args, names...) - - // communication channels - jobCompletedChan := make(chan struct{}) - timeoutChan := time.After(facterTimeout) - - // closure to get data from command - var err error - output := make([]byte, 0, 1024) - - // execute command and capture the output - go func() { - output, err = exec.Command(cmdConfig.executable, args...).Output() - jobCompletedChan <- struct{}{} - }() - - // wait for done - select { - case <-timeoutChan: - // timeout - return nil, errors.New("Facter plugin: fact gathering timeout") - case <-jobCompletedChan: - // success - } - - if err != nil { - log.Printf("Exec failed: %s\n", err) - return nil, err - } - - // place to unmarsha values received from Facter - facts := make(facts) - - // parse output - err = json.Unmarshal(output, &facts) - if err != nil { - log.Printf("Unmarshal failed: %s\n", err) - return nil, err - } - return facts, nil -} diff --git a/plugin/collector/pulse-collector-facter/facter/cmd_test.go b/plugin/collector/pulse-collector-facter/facter/cmd_test.go deleted file mode 100644 index a8b4c9c45..000000000 --- a/plugin/collector/pulse-collector-facter/facter/cmd_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// +build linux - -/* -http://www.apache.org/licenses/LICENSE-2.0.txt - - -Copyright 2015 Intel Coporation - -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. -*/ - -// Tests for communication with external cmd facter (executable) - -package facter - -import ( - "testing" - "time" - - . "github.com/smartystreets/goconvey/convey" -) - -// 4 seconds because default time for goconvey (5 seconds for test) -const testFacterTimeout = 4 * time.Second - -func TestDefaultConfig(t *testing.T) { - Convey("check default config", t, func() { - cmdConfig := newDefaultCmdConfig() - So(cmdConfig.executable, ShouldEqual, "facter") - So(cmdConfig.options, ShouldResemble, []string{"--json"}) - }) -} - -func TestCmdCommunication(t *testing.T) { - Convey("error when facter binary isn't found", t, func() { - _, err := getFacts([]string{"whatever"}, testFacterTimeout, &cmdConfig{executable: "wrongbin"}) - So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "file not found") // isn't ubuntu specific ? - }) - - Convey("error when facter output isn't parsable", t, func() { - _, err := getFacts([]string{"whatever"}, testFacterTimeout, &cmdConfig{executable: "facter", options: []string{}}) - So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "unexpected end of JSON input") - }) -} - -func TestGetFacts(t *testing.T) { - - Convey("getFacts from real facter", t, func() { - - Convey("time outs", func() { - _, err := getFacts([]string{}, 0*time.Second, nil) - So(err, ShouldNotBeNil) - }) - - Convey("returns all something within given time", func() { - facts, err := getFacts([]string{}, testFacterTimeout, nil) - So(err, ShouldBeNil) - So(facts, ShouldNotBeEmpty) - }) - - Convey("returns right thing when asked eg. kernel => linux", func() { - // 4 seconds because default time for goconvey - facts, err := getFacts([]string{"kernel"}, testFacterTimeout, nil) - So(err, ShouldBeNil) - So(facts, ShouldNotBeEmpty) - So(len(facts), ShouldEqual, 1) - fact, exist := facts["kernel"] - So(exist, ShouldEqual, true) - So(fact, ShouldNotBeNil) - }) - - Convey("returns nil in fact value when for non existing fact", func() { - // 4 seconds because default time for goconvey - facts, err := getFacts([]string{"thereisnosuchfact"}, testFacterTimeout, nil) - So(err, ShouldBeNil) - So(facts, ShouldNotBeEmpty) - So(len(facts), ShouldEqual, 1) - fact, exist := facts["thereisnosuchfact"] - So(exist, ShouldEqual, true) - So(fact, ShouldBeNil) - }) - - }) -} diff --git a/plugin/collector/pulse-collector-facter/facter/facter.go b/plugin/collector/pulse-collector-facter/facter/facter.go deleted file mode 100644 index be9225844..000000000 --- a/plugin/collector/pulse-collector-facter/facter/facter.go +++ /dev/null @@ -1,219 +0,0 @@ -/* -http://www.apache.org/licenses/LICENSE-2.0.txt - - -Copyright 2015 Intel Coporation - -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. -*/ - -/* This modules converts implements Pulse API to become an plugin. - -legend: -- metric - represents value of metric from Pulse side -- fact - represents a value about a system gathered from Facter -- name - is string identifier that refers to metric from the Pulse side, so name points to metric - - Implementation details: - - GetMetricTypes() - + - | +------------------+ - +----v---+ getFacts() | | - | Facter +-------------> ./facter --json | - +----^---+ | (goroutine) | - | +------------------+ - + - CollectMetrics() - - -*/ - -package facter - -import ( - "errors" - "fmt" - "os" - "strings" - "time" - - "github.com/intelsdi-x/pulse/control/plugin" - "github.com/intelsdi-x/pulse/control/plugin/cpolicy" -) - -const ( - // parts of returned namescape - vendor = "intel" - prefix = "facter" - // how long we are caching the date from external binary to prevent overuse of resources - defaultCacheTTL = 60 * time.Second - // how long are we going to cache available types of metrics - defaultMetricTypesTTL = defaultCacheTTL - // timeout we are ready to wait for external binary to gather the data - defaultFacterTimeout = 5 * time.Second -) - -/********** - * Facter * - **********/ - -// Facter implements API to communicate with Pulse -type Facter struct { - ttl time.Duration - // injects implementation for getting facts - defaults to use getFacts from cmd.go - // but allows to replace with fake during tests - getFacts func( - names []string, - facterTimeout time.Duration, - cmdConfig *cmdConfig, - ) (facts, error) - // how much time we are ready to wait for getting result from cmd - // is going to be passed to facterTimeout parameter in getFacts - facterTimeout time.Duration -} - -// make sure that we actually satisify requierd interface -var _ plugin.CollectorPlugin = (*Facter)(nil) - -// NewFacter constructs new Facter with default values -func NewFacter() *Facter { - return &Facter{ - // injection of default implementation for gathering facts from Facter - getFacts: getFacts, - facterTimeout: defaultFacterTimeout, - } -} - -// ------------ Pulse plugin interface implementation -------------- - -// GetMetricTypes returns available metrics types -func (f *Facter) GetMetricTypes() ([]plugin.PluginMetricType, error) { - - // facts composed of entries - facts, err := f.getFacts( - nil, // ask for everything - f.facterTimeout, - nil, //default cmd configuration - ) - if err != nil { - if strings.Contains(err.Error(), "executable file not found") { - // Facter cannot be found. Since this is called on load we should - // not send an error as loading a plugin should not fail based on - // whether or not a dynamic path is set. - return []plugin.PluginMetricType{}, nil - } - return nil, err - } - - // capacity - we are going to return all the facts - metricTypes := make([]plugin.PluginMetricType, 0, len(facts)) - - // create types with given namespace - for name, _ := range facts { - namespace := createNamespace(name) - metricType := plugin.PluginMetricType{Namespace_: namespace} - metricTypes = append(metricTypes, metricType) - } - - return metricTypes, nil -} - -// Collect collects metrics from external binary a returns them in form -// acceptable by Pulse, only returns collects that were asked for and return nothing when asked for none -// the order of requested and received metrics isn't guaranted -func (f *Facter) CollectMetrics(metricTypes []plugin.PluginMetricType) ([]plugin.PluginMetricType, error) { - - // parse and check requested names of metrics - names := []string{} - for _, metricType := range metricTypes { - namespace := metricType.Namespace() - - err := validateNamespace(namespace) - if err != nil { - return nil, err - } - - // name of fact - last part of namespace - name := namespace[2] - names = append(names, name) - } - - if len(names) == 0 { - // nothing request, none returned - // !because returned by value, it would return nil slice - return nil, nil - } - - // facts composed of entries - facts, err := f.getFacts(names, f.facterTimeout, nil) - if err != nil { - return nil, err - } - - // make sure that recevied len of names equals asked - if len(facts) != len(names) { - return nil, errors.New("assertion: getFacts returns more/less than asked!") - } - - host, _ := os.Hostname() - // convert facts into PluginMetrics - metrics := make([]plugin.PluginMetricType, 0, len(facts)) - for name, value := range facts { - namespace := createNamespace(name) - metric := *plugin.NewPluginMetricType(namespace, time.Now(), host, value) - metrics = append(metrics, metric) - } - return metrics, nil -} - -func (f *Facter) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) { - c := cpolicy.New() - rule, _ := cpolicy.NewStringRule("name", false, "bob") - rule2, _ := cpolicy.NewStringRule("password", true) - p := cpolicy.NewPolicyNode() - p.Add(rule) - p.Add(rule2) - c.Add([]string{"intel", "facter", "foo"}, p) - return c, nil -} - -// ------------ helper functions -------------- - -// validateNamespace checks namespace intel(vendor)/facter(prefix)/FACTNAME -func validateNamespace(namespace []string) error { - if len(namespace) != 3 { - return errors.New(fmt.Sprintf("unknown metricType %s (should containt just 3 segments)", namespace)) - } - if namespace[0] != vendor { - return errors.New(fmt.Sprintf("unknown metricType %s (expected vendor %s)", namespace, vendor)) - } - - if namespace[1] != prefix { - return errors.New(fmt.Sprintf("unknown metricType %s (expected prefix %s)", namespace, prefix)) - } - return nil -} - -// namspace returns namespace slice of strings -// composed from: vendor, prefix and fact name -func createNamespace(name string) []string { - return []string{vendor, prefix, name} - -} - -// helper type to deal with json values which additionally stores last update moment -type entry struct { - value interface{} - lastUpdate time.Time -} diff --git a/plugin/collector/pulse-collector-facter/facter/facter_test.go b/plugin/collector/pulse-collector-facter/facter/facter_test.go deleted file mode 100644 index 235331502..000000000 --- a/plugin/collector/pulse-collector-facter/facter/facter_test.go +++ /dev/null @@ -1,192 +0,0 @@ -// +build linux integration - -/* -http://www.apache.org/licenses/LICENSE-2.0.txt - - -Copyright 2015 Intel Coporation - -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. -*/ - -/* -# testing -go test -v github.com/intelsdi-x/pulse/plugin/collector/pulse-collector-facter/facter -*/ -package facter - -import ( - "errors" - "strings" - "testing" - "time" - - "github.com/intelsdi-x/pulse/control/plugin" - . "github.com/smartystreets/goconvey/convey" -) - -// fact expected to be available on every system -// can be allways received from Facter for test purposes -const someFact = "kernel" -const someValue = "linux 1234" - -var existingNamespace = []string{vendor, prefix, someFact} - -func TestFacterCollectMetrics(t *testing.T) { - Convey("TestFacterCollect tests", t, func() { - - f := NewFacter() - // always return at least one metric - f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) { - return facts{someFact: someValue}, nil - } - - Convey("asked for nothgin returns nothing", func() { - metricTypes := []plugin.PluginMetricType{} - metrics, err := f.CollectMetrics(metricTypes) - So(err, ShouldBeNil) - So(metrics, ShouldBeEmpty) - }) - - Convey("asked for somehting returns something", func() { - metricTypes := []plugin.PluginMetricType{ - plugin.PluginMetricType{ - Namespace_: existingNamespace, - }, - } - metrics, err := f.CollectMetrics(metricTypes) - So(err, ShouldBeNil) - So(metrics, ShouldNotBeEmpty) - So(len(metrics), ShouldEqual, 1) - - // check just one metric - metric := metrics[0] - So(metric.Namespace()[2], ShouldResemble, someFact) - So(metric.Data().(string), ShouldEqual, someValue) - }) - - Convey("ask for inappriopriate metrics", func() { - Convey("wrong number of parts", func() { - _, err := f.CollectMetrics( - []plugin.PluginMetricType{ - plugin.PluginMetricType{ - Namespace_: []string{"where are my other parts"}, - }, - }, - ) - So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "segments") - }) - - Convey("wrong vendor", func() { - _, err := f.CollectMetrics( - []plugin.PluginMetricType{ - plugin.PluginMetricType{ - Namespace_: []string{"nonintelvendor", prefix, someFact}, - }, - }, - ) - So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "expected vendor") - }) - - Convey("wrong prefix", func() { - _, err := f.CollectMetrics( - []plugin.PluginMetricType{ - plugin.PluginMetricType{ - Namespace_: []string{vendor, "this is wrong prefix", someFact}, - }, - }, - ) - So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "expected prefix") - }) - - }) - }) -} - -func TestFacterInvalidBehavior(t *testing.T) { - - Convey("returns errors as expected when cmd isn't working", t, func() { - f := NewFacter() - // mock that getFacts returns error every time - f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) { - return nil, errors.New("dummy error") - } - - _, err := f.CollectMetrics([]plugin.PluginMetricType{ - plugin.PluginMetricType{ - Namespace_: existingNamespace, - }, - }, - ) - So(err, ShouldNotBeNil) - - _, err = f.GetMetricTypes() - So(err, ShouldNotBeNil) - }) - Convey("returns not as much values as asked", t, func() { - - f := NewFacter() - // mock that getFacts returns error every time - //returns zero elements even when asked for one - f.getFacts = func(_ []string, _ time.Duration, _ *cmdConfig) (facts, error) { - return nil, nil - } - - _, err := f.CollectMetrics([]plugin.PluginMetricType{ - plugin.PluginMetricType{ - Namespace_: existingNamespace, - }, - }, - ) - So(err, ShouldNotBeNil) - So(err.Error(), ShouldContainSubstring, "more/less") - }) - -} - -func TestFacterGetMetricsTypes(t *testing.T) { - - Convey("GetMetricTypes functionallity", t, func() { - - f := NewFacter() - - Convey("GetMetricsTypes returns no error", func() { - // exectues without error - metricTypes, err := f.GetMetricTypes() - So(err, ShouldBeNil) - Convey("metricTypesReply should contain more than zero metrics", func() { - So(metricTypes, ShouldNotBeEmpty) - }) - - Convey("at least one metric contains metric namespace \"intel/facter/kernel\"", func() { - - expectedNamespaceStr := strings.Join(existingNamespace, "/") - - found := false - for _, metricType := range metricTypes { - // join because we cannot compare slices - if strings.Join(metricType.Namespace(), "/") == expectedNamespaceStr { - found = true - break - } - } - if !found { - t.Error("It was expected to find at least on intel/facter/kernel metricType (but it wasn't there)") - } - }) - }) - }) -} diff --git a/plugin/collector/pulse-collector-facter/main.go b/plugin/collector/pulse-collector-facter/main.go deleted file mode 100644 index b4f9e414f..000000000 --- a/plugin/collector/pulse-collector-facter/main.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -http://www.apache.org/licenses/LICENSE-2.0.txt - - -Copyright 2015 Intel Coporation - -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 main - -import ( - "os" - // Import the pulse plugin library - "github.com/intelsdi-x/pulse/control/plugin" - // Import our collector plugin implementation - "github.com/intelsdi-x/pulse/plugin/collector/pulse-collector-facter/facter" -) - -// meta date about plugin -const ( - name = "Intel Fact Gathering Plugin" - version = 1 - pluginType = plugin.CollectorPluginType -) - -// plugin bootstrap -func main() { - plugin.Start( - plugin.NewPluginMeta(name, version, pluginType, []string{plugin.PulseGOBContentType}, []string{plugin.PulseGOBContentType}), - facter.NewFacter(), // CollectorPlugin interface - os.Args[1], - ) -} diff --git a/plugin/collector/pulse-collector-facter/main_test.go b/plugin/collector/pulse-collector-facter/main_test.go deleted file mode 100644 index 4b1a00fde..000000000 --- a/plugin/collector/pulse-collector-facter/main_test.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -http://www.apache.org/licenses/LICENSE-2.0.txt - - -Copyright 2015 Intel Coporation - -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 main - -import ( - "fmt" - "os" - "path" - "testing" - - "github.com/intelsdi-x/pulse/control" - "github.com/intelsdi-x/pulse/plugin/helper" - . "github.com/smartystreets/goconvey/convey" -) - -var ( - PluginName = "pulse-collector-facter" - PluginType = "collector" - PulsePath = os.Getenv("PULSE_PATH") - PluginPath = path.Join(PulsePath, "plugin", PluginName) -) - -func TestPluginLoads(t *testing.T) { - // These tests only work if PULSE_PATH is known. - // It is the responsibility of the testing framework to - // build the plugins first into the build dir. - if PulsePath != "" { - // Helper plugin trigger build if possible for this plugin - helper.BuildPlugin(PluginType, PluginName) - // - Convey("ensure plugin loads and responds", t, func() { - c := control.New() - c.Start() - _, err := c.Load(PluginPath) - - So(err, ShouldBeNil) - }) - } else { - fmt.Printf("PULSE_PATH not set. Cannot test %s plugin.\n", PluginName) - } -} - -func TestMain(t *testing.T) { - Convey("ensure plugin loads and responds", t, func() { - os.Args = []string{"", "{\"NoDaemon\": true}"} - So(func() { main() }, ShouldNotPanic) - }) -}